#include "common.h"
#include <include/svg/SkSVGCanvas.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>

void initCanvas(py::module &m) {
py::class_<SkAutoCanvasRestore>(m, "AutoCanvasRestore", R"docstring(
    Stack helper class calls :py:meth:`Canvas.restoreToCount` when
    :py:class:`AutoCanvasRestore` goes out of scope.

    Use this to guarantee that the canvas is restored to a known state.

    Example::

        with skia.AutoCanvasRestore(canvas):
            canvas.drawCircle(50., 50., 10., paint)

    )docstring")
    .def(py::init<SkCanvas*, bool>(),
        R"docstring(
        Preserves :py:meth:`Canvas.save` count.

        Optionally saves :py:class:`Canvas` clip and :py:class:`Canvas` matrix.

        :param skia.Canvas canvas: :py:class:`Canvas` to guard
        :param bool doSave: call :py:meth:`Canvas.save`
        :return: utility to restore :py:class:`Canvas` state on destructor
        )docstring",
        py::arg("canvas"), py::arg("doSave") = true,
        py::keep_alive<0, 1>())
    .def("restore", &SkAutoCanvasRestore::restore,
        R"docstring(
        Restores :py:class:`Canvas` to saved state immediately.

        Subsequent calls and destructor have no effect.
        )docstring")
    .def("__enter__", [] (SkAutoCanvasRestore& self) {})
    .def("__exit__",
        [] (SkAutoCanvasRestore& self, py::args args) { self.restore(); })
    ;

py::enum_<SkClipOp>(m, "ClipOp")
    .value("kDifference", SkClipOp::kDifference)
    .value("kIntersect", SkClipOp::kIntersect)
    .export_values();

py::class_<SkCanvas> canvas(m, "Canvas", R"docstring(
    :py:class:`Canvas` provides an interface for drawing, and how the drawing is
    clipped and transformed.

    :py:class:`Canvas` contains a stack of :py:class:`Matrix` and clip values.

    :py:class:`Canvas` and :py:class:`Paint` together provide the state to draw
    into :py:class:`Surface` or :py:class:`BaseDevice`. Each :py:class:`Canvas`
    draw call transforms the geometry of the object by the concatenation of all
    :py:class:`Matrix` values in the stack. The transformed geometry is clipped
    by the intersection of all of clip values in the stack. The
    :py:class:`Canvas` draw calls use :py:class:`Paint` to supply drawing state
    such as color, :py:class:`Typeface`, text size, stroke width,
    :py:class:`Shader` and so on.

    To draw to a pixel-based destination, create raster surface or GPU surface.
    Request :py:class:`Canvas` from :py:class:`Surface` to obtain the interface
    to draw. :py:class:`Canvas` generated by raster surface draws to memory
    visible to the CPU. :py:class:`Canvas` generated by GPU surface uses Vulkan
    or OpenGL to draw to the GPU.

    To draw to a document, obtain :py:class:`Canvas` from SVG canvas, document
    PDF, or :py:class:`PictureRecorder`. :py:class:`Document` based
    :py:class:`Canvas` and other :py:class:`Canvas` subclasses reference
    :py:class:`BaseDevice` describing the destination.

    :py:class:`Canvas` can be constructed to draw to :py:class:`Bitmap` without
    first creating raster surface. This approach may be deprecated in the
    future.
    )docstring");

py::enum_<SkCanvas::SrcRectConstraint>(canvas, "SrcRectConstraint")
    .value("kStrict_SrcRectConstraint",
        SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint,
        R"docstring(
        sample only inside bounds; slower
        )docstring")
    .value("kFast_SrcRectConstraint",
        SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint,
        R"docstring(
        sample outside bounds; faster
        )docstring")
    .export_values();

py::enum_<SkCanvas::PointMode>(canvas, "PointMode")
    .value("kPoints_PointMode", SkCanvas::PointMode::kPoints_PointMode,
        R"docstring(
        draw each point separately
        )docstring")
    .value("kLines_PointMode", SkCanvas::PointMode::kLines_PointMode,
        R"docstring(
        draw each pair of points as a line segment
        )docstring")
    .value("kPolygon_PointMode", SkCanvas::PointMode::kPolygon_PointMode,
        R"docstring(
        draw the array of points as a open polygon
        )docstring")
    .export_values();

py::enum_<SkCanvas::QuadAAFlags>(canvas, "QuadAAFlags")
    .value("kLeft_QuadAAFlag", SkCanvas::QuadAAFlags::kLeft_QuadAAFlag)
    .value("kTop_QuadAAFlag", SkCanvas::QuadAAFlags::kTop_QuadAAFlag)
    .value("kRight_QuadAAFlag", SkCanvas::QuadAAFlags::kRight_QuadAAFlag)
    .value("kBottom_QuadAAFlag", SkCanvas::QuadAAFlags::kBottom_QuadAAFlag)
    .value("kNone_QuadAAFlags", SkCanvas::QuadAAFlags::kNone_QuadAAFlags)
    .value("kAll_QuadAAFlags", SkCanvas::QuadAAFlags::kAll_QuadAAFlags)
    .export_values();

py::enum_<SkCanvas::SaveLayerFlagsSet>(
    canvas, "SaveLayerFlags", py::arithmetic())
    .value("kPreserveLCDText_SaveLayerFlag",
        SkCanvas::SaveLayerFlagsSet::kPreserveLCDText_SaveLayerFlag)
    .value("kInitWithPrevious_SaveLayerFlag",
        SkCanvas::SaveLayerFlagsSet::kInitWithPrevious_SaveLayerFlag,
        "initializes with previous contents")
    .value("kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag",
        SkCanvas::SaveLayerFlagsSet::
        kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag,
        "experimental: do not use")
    .value("kF16ColorType",
        SkCanvas::SaveLayerFlagsSet::kF16ColorType)
    .export_values();

py::class_<SkCanvas::SaveLayerRec>(canvas, "SaveLayerRec",
    R"docstring(
    SaveLayerRec contains the state used to create the layer.
    )docstring")
    .def(py::init<>(),
        R"docstring(
        Sets :py:attr:`fBounds`, :py:attr:`fPaint`, and :py:attr:`fBackdrop` to
        nullptr.

        Clears :py:attr:`fSaveLayerFlags`.
        )docstring")
    .def(py::init<const SkRect*, const SkPaint*, SkCanvas::SaveLayerFlags>(),
        R"docstring(
        Sets :py:attr:`fBounds`, :py:attr:`fPaint`, and
        :py:attr:`fSaveLayerFlags`; sets :py:attr:`fBackdrop` to nullptr.

        :bounds: layer dimensions; may be nullptr
        :paint: applied to layer when overlaying prior layer;
            may be nullptr
        :saveLayerFlags: SaveLayerRec options to
            modify layer
        )docstring",
        py::arg("bounds"), py::arg("paint"), py::arg("saveLayerFlags") = 0)
    .def(py::init<const SkRect*, const SkPaint*, const SkImageFilter*,
        SkCanvas::SaveLayerFlags>(),
        R"docstring(
        Sets :py:attr:`fBounds`, :py:attr:`fPaint`, :py:attr:`fBackdrop`, and
        :py:attr:`fSaveLayerFlags`.

        :bounds: layer dimensions; may be nullptr
        :paint: applied to layer when overlaying prior layer;
            may be nullptr
        :backdrop: If not null, this causes the current
            layer to be filtered by backdrop, and then drawn into the new layer
            (respecting the current clip). If null, the new layer is initialized
            with transparent-black.
        :saveLayerFlags: SaveLayerRec options to
            modify layer
        )docstring",
        py::arg("bounds"), py::arg("paint"), py::arg("backdrop"),
        py::arg("saveLayerFlags"))
    .def_readwrite("fBounds", &SkCanvas::SaveLayerRec::fBounds,
        R"docstring(
        hints at layer size limit
        )docstring")
    .def_readwrite("fPaint", &SkCanvas::SaveLayerRec::fPaint,
        R"docstring(
        modifies overlay
        )docstring")
    .def_readwrite("fBackdrop", &SkCanvas::SaveLayerRec::fBackdrop,
        R"docstring(
        If not null, this triggers the same initialization behavior as setting
        :py:attr:`Canvas.SaveLayerFlags.kInitWithPrevious` on
        :py:attr:`fSaveLayerFlags`: the current layer is copied into the new
        layer, rather than initializing the new layer with transparent-black.

        This is then filtered by fBackdrop (respecting the current clip).
        )docstring")
    .def_readwrite("fSaveLayerFlags", &SkCanvas::SaveLayerRec::fSaveLayerFlags,
        R"docstring(
        preserves LCD text, creates with prior layer contents
        )docstring")
    ;

py::class_<SkCanvas::Lattice> lattice(canvas, "Lattice", R"docstring(
    :py:class:`Canvas.Lattice` divides :py:class:`Bitmap` or :py:class:`Image`
    into a rectangular grid.

    Grid entries on even columns and even rows are fixed; these entries are
    always drawn at their original size if the destination is large enough. If
    the destination side is too small to hold the fixed entries, all fixed
    entries are proportionately scaled down to fit. The grid entries not on even
    columns and rows are scaled to fit the remaining space, if any.
    )docstring");

py::enum_<SkCanvas::Lattice::RectType>(lattice, "RectType")
    .value("kDefault", SkCanvas::Lattice::RectType::kDefault,
        R"docstring(
        draws :py:class:`Bitmap` into lattice rectangle
        )docstring")
    .value("kTransparent", SkCanvas::Lattice::RectType::kTransparent,
        R"docstring(
        skips lattice rectangle by making it transparent
        )docstring")
    .value("kFixedColor", SkCanvas::Lattice::RectType::kFixedColor,
        R"docstring(
        draws one of fColors into lattice rectangle
        )docstring")
    .export_values();

lattice
    // .def(py::init([] (std::, py::list, SkCanvas::Lattice::RectType rectType,
    //     const SkIRect *bounds, const SkColor* colors) {
    //     return SkCanvas::Lattice lattice;
    // }))
    .def_readwrite("fXDivs", &SkCanvas::Lattice::fXDivs,
        R"docstring(
        x-axis values dividing bitmap
        )docstring")
    .def_readwrite("fYDivs", &SkCanvas::Lattice::fYDivs,
        R"docstring(
        y-axis values dividing bitmap
        )docstring")
    .def_readwrite("fRectTypes", &SkCanvas::Lattice::fRectTypes,
        R"docstring(
        array of fill types
        )docstring")
    .def_readwrite("fXCount", &SkCanvas::Lattice::fXCount,
        R"docstring(
        number of x-coordinates
        )docstring")
    .def_readwrite("fYCount", &SkCanvas::Lattice::fYCount,
        R"docstring(
        number of y-coordinates
        )docstring")
    .def_readwrite("fBounds", &SkCanvas::Lattice::fBounds,
        R"docstring(
        source bounds to draw from
        )docstring")
    .def_readwrite("fColors", &SkCanvas::Lattice::fColors,
        R"docstring(
        array of colors
        )docstring")
    ;

canvas
    .def("__repr__",
        [] (const SkCanvas& canvas) {
            auto size = canvas.getBaseLayerSize();
            return py::str("Canvas({}, {}, saveCount={})").format(
                size.width(), size.height(), canvas.getSaveCount());
        })
    .def(py::init<>(),
        R"docstring(
        Creates an empty :py:class:`Canvas` with no backing device or pixels,
        with a width and height of zero.
        )docstring")
    .def(py::init(
        [] (py::array array, SkColorType ct, SkAlphaType at,
            const SkColorSpace* cs, const SkSurfaceProps *surfaceProps) {
            auto imageInfo = NumPyToImageInfo(array, ct, at, cs);
            auto canvas = SkCanvas::MakeRasterDirect(
                imageInfo, array.mutable_data(), array.strides(0),
                surfaceProps);
            if (!canvas)
                throw std::runtime_error("Failed to create Canvas");
            return canvas;
        }),
        R"docstring(
        Creates raster :py:class:`Canvas` backed by numpy array.

        Subsequent :py:class:`Canvas` calls draw into pixels. To access pixels
        after drawing, call flush() or peekPixels().

        :array: numpy ndarray of shape=(height, width, channels) and appropriate
            dtype. Must have non-zero width and height, and the valid number of
            channels for the specified color type.
        :colorType: color type of the array
        :alphaType: alpha type of the array
        :colorSpace: range of colors; may be nullptr
        )docstring",
        py::arg("array"), py::arg("colorType") = kN32_SkColorType,
        py::arg("alphaType") = kUnpremul_SkAlphaType,
        py::arg("colorSpace") = nullptr, py::arg("surfaceProps") = nullptr)
    .def(py::init<int, int, const SkSurfaceProps*>(),
        R"docstring(
        Creates :py:class:`Canvas` of the specified dimensions without a
        :py:class:`Surface`.

        Used by subclasses with custom implementations for draw member
        functions.

        If props equals `None`, :py:class:`SurfaceProps` are created with
        :py:class:`SurfaceProps.InitType` settings, which choose the pixel
        striping direction and order. Since a platform may dynamically change
        its direction when the device is rotated, and since a platform may have
        multiple monitors with different characteristics, it is best not to rely
        on this legacy behavior.

        :width: zero or greater
        :height: zero or greater
        :props: LCD striping orientation and setting for device independent
            fonts; may be `None`
        )docstring",
        py::arg("width"), py::arg("height"), py::arg("props") = nullptr)
    .def(py::init<const SkBitmap&>(),
        R"docstring(
        Constructs a canvas that draws into bitmap.

        Sets :py:attr:`SurfaceProps.kLegacyFontHost` in constructed
        :py:class:`Surface`.

        :py:class:`Bitmap` is copied so that subsequently editing bitmap will
        not affect constructed :py:class:`Canvas`.

        May be deprecated in the future.

        :bitmap: width, height, :py:class:`ColorType`, :py:class:`AlphaType`,
            and pixel storage of raster surface
        )docstring",
        py::arg("bitmap"))
    .def(py::init<const SkBitmap&, const SkSurfaceProps&>(),
        R"docstring(
        Constructs a canvas that draws into bitmap.

        Use props to match the device characteristics, like LCD striping.

        bitmap is copied so that subsequently editing bitmap will not affect
        constructed :py:class:`Canvas`.

        :bitmap: width, height, :py:class:`ColorType`, :py:class:`AlphaType`,
            and pixel storage of raster surface
        :props: order and orientation of RGB striping; and whether to use device
            independent fonts
        )docstring",
        py::arg("bitmap"), py::arg("props"))
    .def("toarray", &ReadToNumpy<SkCanvas>,
        R"docstring(
        Exports a ``numpy.ndarray``.

        :param srcX: offset into readable pixels on x-axis; may be negative
        :param srcY: offset into readable pixels on y-axis; may be negative
        :param colorType: target :py:class:`ColorType`
        :param alphaType: target :py:class:`AlphaType`
        :param colorSpace: target :py:class:`ColorSpace`
        :return: numpy.ndarray
        )docstring",
        py::arg("srcX") = 0, py::arg("srcY") = 0,
        py::arg("colorType") = kUnknown_SkColorType,
        py::arg("alphaType") = kUnpremul_SkAlphaType,
        py::arg("colorSpace") = nullptr)
    .def("imageInfo", &SkCanvas::imageInfo,
        R"docstring(
        Returns :py:class:`ImageInfo` for :py:class:`Canvas`.

        If :py:class:`Canvas` is not associated with raster surface or GPU
        surface, returned :py:class:`ColorType` is set to
        :py:attr:`ColorType.kUnknown_ColorType`.

        :return: dimensions and :py:class:`ColorType` of :py:class:`Canvas`
        )docstring")
    .def("getProps", &SkCanvas::getProps,
        R"docstring(
        Copies :py:class:`SurfaceProps`, if :py:class:`Canvas` is associated
        with raster surface or GPU surface, and returns true.

        Otherwise, returns false and leave props unchanged.

        :param skia.SurfaceProps props: storage for writable SurfaceProps
        :return: true if :py:class:`SurfaceProps` was copied
        )docstring",
        py::arg("props"))
    .def("flush", &SkCanvas::flush,
        R"docstring(
        Triggers the immediate execution of all pending draw operations.

        If :py:class:`Canvas` is associated with GPU surface, resolves all
        pending GPU operations. If :py:class:`Canvas` is associated with raster
        surface, has no effect; raster draw operations are never deferred.
        )docstring")
    .def("getBaseLayerSize", &SkCanvas::getBaseLayerSize,
        R"docstring(
        Gets the size of the base or root layer in global canvas coordinates.

        The origin of the base layer is always (0,0). The area available for
        drawing may be smaller (due to clipping or saveLayer).

        :return: integral width and height of base layer
        :rtype: skia.ISize
        )docstring")
    .def("makeSurface", &SkCanvas::makeSurface,
        R"docstring(
        Creates :py:class:`Surface` matching info and props, and associates it
        with :py:class:`Canvas`.

        Returns nullptr if no match found.

        If props is nullptr, matches :py:class:`SurfaceProps` in
        :py:class:`Canvas`. If props is nullptr and :py:class:`Canvas` does not
        have :py:class:`SurfaceProps`, creates :py:class:`Surface` with default
        :py:class:`SurfaceProps`.

        :param skia.ImageInfo info: width, height, :py:class:`ColorType`,
            :py:class:`AlphaType`, and :py:class:`ColorSpace`
        :param skia.SurfaceProps props: :py:class:`SurfaceProps` to match; may
            be nullptr to match :py:class:`Canvas`
        :return: SkSurface matching info and props, or nullptr if no match is
            available
        :rtype: skia.Surface or None
        )docstring",
        py::arg("info"), py::arg("props") = nullptr)
    .def("getSurface", &SkCanvas::getSurface,
        R"docstring(
        Sometimes a canvas is owned by a surface.

        If it is, getSurface() will return a bare pointer to that surface, else
        this will return nullptr.

        :rtype: skia.Surface or None
        )docstring",
        py::return_value_policy::reference)
    .def("accessTopLayerPixels",
        [] (SkCanvas& canvas, SkIPoint* origin) -> py::object {
            SkImageInfo info;
            size_t rowBytes;
            void* addr = canvas.accessTopLayerPixels(&info, &rowBytes, origin);
            if (!addr)
                return py::none();
            py::ssize_t bytesPerPixel = info.bytesPerPixel();
            const char* format =
                (bytesPerPixel == 1) ?
                    py::format_descriptor<uint8_t>::value :
                (bytesPerPixel == 2) ?
                    py::format_descriptor<uint16_t>::value :
                (bytesPerPixel == 4) ?
                    py::format_descriptor<uint32_t>::value :
                (bytesPerPixel == 8) ?
                    py::format_descriptor<uint64_t>::value :
                py::format_descriptor<uint8_t>::value;
            return py::memoryview::from_buffer(
                addr,
                bytesPerPixel,
                format,
                { info.width(), info.height() },
                { py::ssize_t(rowBytes), bytesPerPixel },
                true
            );
        },
        R"docstring(
        Returns the pixel base address, and origin if the pixels can be read
        directly.

        The returned address is only valid while :py:class:`Canvas` is in scope
        and unchanged. Any :py:class:`Canvas` call or :py:class:`Surface` call
        may invalidate the returned address and other returned values.

        If pixels are inaccessible, returns None.

        :param origin: storage for :py:class:`Canvas` top layer origin, its
            top-left corner; may be nullptr
        :return: address of pixels, or nullptr if inaccessible
        )docstring",
        py::arg("origin") = nullptr)
    // .def("accessTopRasterHandle", &SkCanvas::accessTopRasterHandle,
    //     "Returns custom context that tracks the SkMatrix and clip.")
    .def("peekPixels", &SkCanvas::peekPixels,
        R"docstring(
        Returns true if :py:class:`Canvas` has direct access to its pixels.

        Pixels are readable when :py:class:`BaseDevice` is raster. Pixels are
        not readable when :py:class:`Canvas` is returned from GPU surface,
        returned by :py:class:`Document`::beginPage, returned by
        :py:class:`PictureRecorder`::beginRecording, or :py:class:`Canvas` is
        the base of a utility class like DebugCanvas.

        pixmap is valid only while :py:class:`Canvas` is in scope and unchanged.
        Any :py:class:`Canvas` or :py:class:`Surface` call may invalidate the
        pixmap values.

        :param skia.Pixmap pixmap: storage for pixel state if pixels are
            readable; otherwise, ignored
        :return: true if :py:class:`Canvas` has direct access to pixels
        )docstring",
        py::arg("pixmap"))
    .def("readPixels", &ReadPixels<SkCanvas>,
        R"docstring(
        Copies :py:class:`Rect` of pixels from :py:class:`Canvas` into
        dstPixels.

        :py:class:`Matrix` and clip are ignored.

        Source :py:class:`Rect` corners are (srcX, srcY) and (
        imageInfo().width(), imageInfo().height()). Destination :py:class:`Rect`
        corners are (0, 0) and (array.shape[1], array.shape[0]). Copies each
        readable pixel intersecting both rectangles, without scaling, converting
        to :py:attr:`ColorType.kN32_ColorType` and
        :py:attr:`AlphaType.kPremul_AlphaType` if required.

        Pixels are readable when :py:class:`BaseDevice` is raster, or backed by
        a GPU. Pixels are not readable when :py:class:`Canvas` is returned by
        :py:meth:`Document.beginPage`, returned by
        :py:meth:`PictureRecorder.beginRecording`, or :py:class:`Canvas` is the
        base of a utility class like DebugCanvas.

        The destination pixel storage must be allocated by the caller.

        Pixel values are converted only if :py:class:`ColorType` and
        :py:class:`AlphaType` do not match. Only pixels within both source and
        destination rectangles are copied. array contents outside
        :py:class:`Rect` intersection are unchanged.

        Pass negative values for srcX or srcY to offset pixels across or down
        destination.

        Does not copy, and returns false if:

        - Source and destination rectangles do not intersect.
        - :py:class:`Canvas` pixels could not be converted to
            :py:attr:`ColorType.kN32_ColorType` or
            :py:attr:`AlphaType.kPremul_AlphaType`.
        - :py:class:`Canvas` pixels are not readable; for instance,
        - :py:class:`Canvas` is document-based.

        :dstInfo: width, height, :py:class:`ColorType`, and
            :py:class:`AlphaType` of dstPixels
        :dstPixels: storage for pixels; dstInfo.height() times dstRowBytes, or
            larger
        :dstRowBytes: size of one destination row; dstInfo.width() times pixel
            size, or larger. Ignored when dstPixels has more than one-dimension.
        :srcX: offset into readable pixels on x-axis; may be negative
        :srcY: offset into readable pixels on y-axis; may be negative
        :return: true if pixels were copied
        )docstring",
        py::arg("dstInfo"), py::arg("dstPixels"), py::arg("dstRowBytes") = 0,
        py::arg("srcX") = 0, py::arg("srcY") = 0)
    .def("readPixels",
        py::overload_cast<const SkPixmap&, int, int>(&SkCanvas::readPixels),
        R"docstring(
        Copies :py:class:`Rect` of pixels from :py:class:`Canvas` into pixmap.

        :py:class:`Matrix` and clip are ignored.

        Source :py:class:`Rect` corners are (srcX, srcY) and (
        imageInfo().width(), imageInfo().height()). Destination
        :py:class:`Rect` corners are (0, 0) and (pixmap.width(),
        pixmap.height()). Copies each readable pixel intersecting both
        rectangles, without scaling, converting to pixmap.colorType() and
        pixmap.alphaType() if required.

        Pixels are readable when :py:class:`BaseDevice` is raster, or backed by
        a GPU. Pixels are not readable when :py:class:`Canvas` is returned by
        :py:meth:`Document.beginPage`, returned by
        :py:meth:`PictureRecorder.beginRecording`, or :py:class:`Canvas` is the
        base of a utility class like DebugCanvas.

        Caller must allocate pixel storage in pixmap if needed.

        Pixel values are converted only if :py:class:`ColorType` and
        :py:class:`AlphaType` do not match. Only pixels within both source and
        destination :py:class:`Rect` are copied. pixmap pixels contents outside
        :py:class:`Rect` intersection are unchanged.

        Pass negative values for srcX or srcY to offset pixels across or down
        pixmap.

        Does not copy, and returns false if:

        - Source and destination rectangles do not intersect.
        - :py:class:`Canvas` pixels could not be converted to
            pixmap.colorType() or pixmap.alphaType().
        - :py:class:`Canvas` pixels are not readable; for instance,
            :py:class:`Canvas` is document-based.
        - :py:class:`Pixmap` pixels could not be allocated.
        - pixmap.rowBytes() is too small to contain one row of pixels.

        :pixmap: storage for pixels copied from
            :py:class:`Canvas`
        :srcX: offset into readable pixels on x-axis; may be negative
        :srcY: offset into readable pixels on y-axis; may be negative
        :return: true if pixels were copied
        )docstring",
        py::arg("pixmap"), py::arg("srcX") = 0, py::arg("srcY") = 0)
    .def("readPixels",
        py::overload_cast<const SkBitmap&, int, int>(&SkCanvas::readPixels),
        R"docstring(
        Copies :py:class:`Rect` of pixels from :py:class:`Canvas` into bitmap.

        :py:class:`Matrix` and clip are ignored.

        Source :py:class:`Rect` corners are (srcX, srcY) and (
        imageInfo().width(), imageInfo().height()). Destination
        :py:class:`Rect` corners are (0, 0) and (bitmap.width(),
        bitmap.height()). Copies each readable pixel intersecting both
        rectangles, without scaling, converting to bitmap.colorType() and
        bitmap.alphaType() if required.

        Pixels are readable when :py:class:`BaseDevice` is raster, or backed by
        a GPU. Pixels are not readable when :py:class:`Canvas` is returned by
        :py:meth:`Document.beginPage`, returned by
        :py:meth:`PictureRecorder.beginRecording`, or :py:class:`Canvas` is the
        base of a utility class like DebugCanvas.

        Caller must allocate pixel storage in bitmap if needed.

        :py:class:`Bitmap` values are converted only if :py:class:`ColorType`
        and :py:class:`AlphaType` do not match. Only pixels within both source
        and destination rectangles are copied. :py:class:`Bitmap` pixels outside
        :py:class:`Rect` intersection are unchanged.

        Pass negative values for srcX or srcY to offset pixels across or down
        bitmap.

        Does not copy, and returns false if:

        - Source and destination rectangles do not intersect.
        - :py:class:`Canvas` pixels could not be converted to bitmap.colorType()
            or bitmap.alphaType().
        - :py:class:`Canvas` pixels are not readable; for instance,
            :py:class:`Canvas` is document-based.
        - bitmap pixels could not be allocated.
        - bitmap.rowBytes() is too small to contain one row of pixels.

        :bitmap: storage for pixels copied from
            :py:class:`Canvas`
        :srcX: offset into readable pixels on x-axis; may be negative
        :srcY: offset into readable pixels on y-axis; may be negative

        :return: true if pixels were copied
        )docstring",
        py::arg("bitmap"), py::arg("srcX") = 0, py::arg("srcY") = 0)
    .def("writePixels",
        [] (SkCanvas& canvas, const SkImageInfo& imageInfo, py::buffer pixels,
            size_t srcRowBytes, int x, int y) {
            auto info = pixels.request();
            auto rowBytes = ValidateBufferToImageInfo(
                imageInfo, info, srcRowBytes);
            return canvas.writePixels(imageInfo, info.ptr, rowBytes, x, y);
        },
        R"docstring(
        Copies :py:class:`Rect` from pixels to :py:class:`Canvas`.

        :py:class:`Matrix` and clip are ignored. Source :py:class:`Rect` corners
        are (0, 0) and (info.width(), info.height()). Destination
        :py:class:`Rect` corners are (x, y) and (imageInfo().width(),
        imageInfo().height()).

        Copies each readable pixel intersecting both rectangles, without
        scaling, converting to imageInfo().colorType() and
        imageInfo().alphaType() if required.

        Pixels are writable when :py:class:`BaseDevice` is raster, or backed by
        a GPU. Pixels are not writable when :py:class:`Canvas` is returned by
        :py:meth:`Document.beginPage`, returned by
        :py:meth:`PictureRecorder.beginRecording`, or :py:class:`Canvas` is the
        base of a utility class like DebugCanvas.

        Pixel values are converted only if :py:class:`ColorType` and
        :py:class:`AlphaType` do not match. Only pixels within both source and
        destination rectangles are copied. :py:class:`Canvas` pixels outside
        :py:class:`Rect` intersection are unchanged.

        Pass negative values for x or y to offset pixels to the left or above
        :py:class:`Canvas` pixels.

        Does not copy, and returns false if:

        - Source and destination rectangles do not intersect.
        - pixels could not be converted to :py:class:`Canvas`
            imageInfo().colorType() or imageInfo().alphaType().
        - :py:class:`Canvas` pixels are not writable; for instance,
            :py:class:`Canvas` is document-based.
        - rowBytes is too small to contain one row of pixels.

        :array: pixels to copy, in native 32-bit colors.
        :x: offset into :py:class:`Canvas` writable pixels on x-axis; may be
            negative
        :y: offset into :py:class:`Canvas` writable pixels on y-axis; may be
            negative

        :return: true if pixels were written to :py:class:`Canvas`
        )docstring",
        py::arg("info"), py::arg("pixels"), py::arg("rowBytes") = 0,
        py::arg("x") = 0, py::arg("y") = 0)
    .def("writePixels",
        py::overload_cast<const SkBitmap&, int, int>(&SkCanvas::writePixels),
        R"docstring(
        Copies :py:class:`Rect` from pixels to :py:class:`Canvas`.

        :py:class:`Matrix` and clip are ignored. Source :py:class:`Rect` corners
        are (0, 0) and (bitmap.width(), bitmap.height()).

        Destination :py:class:`Rect` corners are (x, y) and
        (imageInfo().width(), imageInfo().height()).

        Copies each readable pixel intersecting both rectangles, without
        scaling, converting to imageInfo().colorType() and
        imageInfo().alphaType() if required.

        Pixels are writable when :py:class:`BaseDevice` is raster, or backed by
        a GPU. Pixels are not writable when :py:class:`Canvas` is returned by
        :py:meth:`Document.beginPage`, returned by
        :py:meth:`PictureRecorder.beginRecording`, or :py:class:`Canvas` is the
        base of a utility class like DebugCanvas.

        Pixel values are converted only if :py:class:`ColorType` and
        :py:class:`AlphaType` do not match. Only pixels within both source and
        destination rectangles are copied. :py:class:`Canvas` pixels outside
        :py:class:`Rect` intersection are unchanged.

        Pass negative values for x or y to offset pixels to the left or above
        :py:class:`Canvas` pixels.

        Does not copy, and returns false if:

        - Source and destination rectangles do not intersect.
        - bitmap does not have allocated pixels.
        - bitmap pixels could not be converted to
            :py:meth:`Canvas.imageInfo()`.colorType() or alphaType().
        - :py:class:`Canvas` pixels are not writable; for instance,
            :py:class:`Canvas` is document-based.
        - bitmap pixels are inaccessible; for instance, bitmap wraps a texture.

        :bitmap: contains pixels copied to :py:class:`Canvas`.
        :x: offset into :py:class:`Canvas` writable pixels on x-axis; may be
            negative
        :y: offset into :py:class:`Canvas` writable pixels on y-axis; may be
            negative

        :return: true if pixels were written to :py:class:`Canvas`
        )docstring",
        py::arg("bitmap"), py::arg("x") = 0, py::arg("y") = 0)
    .def("save", &SkCanvas::save,
        R"docstring(
        Saves :py:class:`Matrix` and clip.

        Calling :py:meth:`restore` discards changes to :py:class:`Matrix` and
        clip, restoring the :py:class:`Matrix` and clip to their state when
        :py:meth:`save` was called.

        :py:class:`Matrix` may be changed by :py:meth:`translate`,
        :py:meth:`scale`, :py:meth:`rotate`, :py:meth:`skew`, :py:meth:`concat`,
        :py:meth:`setMatrix`, and :py:meth:`resetMatrix`. Clip may be changed by
        :py:meth:`clipRect`, :py:meth:`clipRRect`, :py:meth:`clipPath`,
        :py:meth:`clipRegion`.

        Saved :py:class:`Canvas` state is put on a stack; multiple calls to
        :py:meth:`save` should be balance by an equal number of calls to
        :py:meth:`restore`.

        Call :py:meth:`restoreToCount` with result to restore this and
        subsequent saves.

        :return: depth of saved stack
        )docstring")
    .def("saveLayer",
        py::overload_cast<const SkRect*, const SkPaint*>(&SkCanvas::saveLayer),
        R"docstring(
        Saves :py:class:`Matrix` and clip, and allocates a :py:class:`Bitmap`
        for subsequent drawing.

        Calling :py:meth:`restore` discards changes to :py:class:`Matrix` and
        clip, and draws the :py:class:`Bitmap`.

        :py:class:`Matrix` may be changed by :py:meth:`translate`,
        :py:meth:`scale`, :py:meth:`rotate`, :py:meth:`skew`, :py:meth:`concat`,
        :py:meth:`setMatrix`, and :py:meth:`resetMatrix`. Clip may be changed by
        :py:meth:`clipRect`, :py:meth:`clipRRect`, :py:meth:`clipPath`,
        :py:meth:`clipRegion`.

        :py:class:`Rect` bounds suggests but does not define the
        :py:class:`Bitmap` size. To clip drawing to a specific rectangle, use
        :py:meth:`clipRect`.

        Optional :py:class:`Paint` paint applies alpha, :py:class:`ColorFilter`,
        :py:class:`ImageFilter`, and :py:class:`BlendMode` when
        :py:meth:`restore` is called.

        Call :py:meth:`restoreToCount` with returned value to restore this and
        subsequent saves.

        :bounds: hint to limit the size of the layer; may be nullptr
        :paint: graphics state for layer; may be nullptr

        :return: depth of saved stack
        )docstring",
        py::arg("bounds") = nullptr, py::arg("paint") = nullptr)
    // .def("saveLayer",
    //     py::overload_cast<const SkRect&, const SkPaint*>(
    //         &SkCanvas::saveLayer),
    //     "Saves SkMatrix and clip, and allocates a SkBitmap for subsequent "
    //     "drawing.")
    .def("saveLayerAlpha", &SkCanvas::saveLayerAlpha,
        R"docstring(
        Saves :py:class:`Matrix` and clip, and allocates a :py:class:`Bitmap`
        for subsequent drawing.

        Calling :py:meth:`restore` discards changes to :py:class:`Matrix` and
        clip, and blends layer with alpha opacity onto prior layer.

        :py:class:`Matrix` may be changed by :py:meth:`translate`,
        :py:meth:`scale`, :py:meth:`rotate`, :py:meth:`skew`, :py:meth:`concat`,
        :py:meth:`setMatrix`, and :py:meth:`resetMatrix`. Clip may be changed by
        :py:meth:`clipRect`, :py:meth:`clipRRect`, :py:meth:`clipPath`,
        :py:meth:`clipRegion`.

        :py:class:`Rect` bounds suggests but does not define the
        :py:class:`Bitmap` size. To clip drawing to a specific rectangle, use
        :py:meth:`clipRect`.

        alpha of zero is fully transparent, 255 is fully opaque.

        Call :py:meth:`restoreToCount` with returned value to restore this and
        subsequent saves.

        :param skia.Rect bounds: hint to limit the size of the layer; may be
            nullptr
        :param int alpha: opacity of layer

        :return: depth of saved stack
        )docstring",
        py::arg("bounds"), py::arg("alpha"))
    .def("saveLayer",
        py::overload_cast<const SkCanvas::SaveLayerRec&>(&SkCanvas::saveLayer),
        R"docstring(
        Saves :py:class:`Matrix` and clip, and allocates a :py:class:`Bitmap`
        for subsequent drawing.

        Calling :py:meth:`restore` discards changes to :py:class:`Matrix` and
        clip, and blends :py:class:`Bitmap` with alpha opacity onto the prior
        layer.

        :py:class:`Matrix` may be changed by :py:meth:`translate`,
        :py:meth:`scale`, :py:meth:`rotate`, :py:meth:`skew`, :py:meth:`concat`,
        :py:meth:`setMatrix`, and :py:meth:`resetMatrix`. Clip may be changed by
        :py:meth:`clipRect`, :py:meth:`clipRRect`, :py:meth:`clipPath`,
        :py:meth:`clipRegion`.

        :py:class:`SaveLayerRec` contains the state used to create the layer.

        Call :py:meth:`restoreToCount` with returned value to restore this and
        subsequent saves.

        :layerRec: layer state

        :return: depth of save state stack before this call was made.
        )docstring",
        py::arg("layerRec"))
    .def("restore", &SkCanvas::restore,
        R"docstring(
        Removes changes to :py:class:`Matrix` and clip since :py:class:`Canvas`
        state was last saved.

        The state is removed from the stack.

        Does nothing if the stack is empty.
        )docstring")
    .def("getSaveCount", &SkCanvas::getSaveCount,
        R"docstring(
        Returns the number of saved states, each containing: :py:class:`Matrix`
        and clip.

        Equals the number of :py:meth:`save` calls less the number of
        :py:meth:`restore` calls plus one. The save count of a new canvas is
        one.

        :return: depth of save state stack
        )docstring")
    .def("restoreToCount", &SkCanvas::restoreToCount,
        R"docstring(
        Restores state to :py:class:`Matrix` and clip values when
        :py:meth:`save`, :py:meth:`saveLayer`,
        :py:meth:`saveLayerPreserveLCDTextRequests`, or
        :py:meth:`saveLayerAlpha` returned saveCount.

        Does nothing if saveCount is greater than state stack count. Restores
        state to initial values if saveCount is less than or equal to one.

        :param int saveCount: depth of state stack to restore
        )docstring",
        py::arg("saveCount"))
    .def("translate", &SkCanvas::translate,
        R"docstring(
        Translates :py:class:`Matrix` by dx along the x-axis and dy along the
        y-axis.

        Mathematically, replaces :py:class:`Matrix` with a translation matrix
        premultiplied with :py:class:`Matrix`.

        This has the effect of moving the drawing by (dx, dy) before
        transforming the result with :py:class:`Matrix`.

        :param dx: distance to translate on x-axis
        :param dy: distance to translate on y-axis
        )docstring",
        py::arg("dx"), py::arg("dy"))
    .def("scale", &SkCanvas::scale,
        R"docstring(
        Scales :py:class:`Matrix` by sx on the x-axis and sy on the y-axis.

        Mathematically, replaces :py:class:`Matrix` with a scale matrix
        premultiplied with :py:class:`Matrix`.

        This has the effect of scaling the drawing by (sx, sy) before
        transforming the result with :py:class:`Matrix`.

        :param float sx: amount to scale on x-axis
        :param float sy: amount to scale on y-axis
        )docstring",
        py::arg("sx"), py::arg("sy"))
    .def("rotate", py::overload_cast<SkScalar>(&SkCanvas::rotate),
        R"docstring(
        Rotates :py:class:`Matrix` by degrees.

        Positive degrees rotates clockwise.

        Mathematically, replaces :py:class:`Matrix` with a rotation matrix
        premultiplied with :py:class:`Matrix`.

        This has the effect of rotating the drawing by degrees before
        transforming the result with :py:class:`Matrix`.

        :degrees: amount to rotate, in degrees
        )docstring",
        py::arg("degrees"))
    .def("rotate",
        py::overload_cast<SkScalar, SkScalar, SkScalar>(&SkCanvas::rotate),
        R"docstring(
        Rotates :py:class:`Matrix` by degrees about a point at (px, py).

        Positive degrees rotates clockwise.

        Mathematically, constructs a rotation matrix; premultiplies the rotation
        matrix by a translation matrix; then replaces :py:class:`Matrix` with
        the resulting matrix premultiplied with :py:class:`Matrix`.

        This has the effect of rotating the drawing about a given point before
        transforming the result with :py:class:`Matrix`.

        :degrees: amount to rotate, in degrees
        :px: x-axis value of the point to rotate about
        :py: y-axis value of the point to rotate about
        )docstring",
        py::arg("degrees"), py::arg("px"), py::arg("py"))
    .def("skew", &SkCanvas::skew,
        R"docstring(
        Skews :py:class:`Matrix` by sx on the x-axis and sy on the y-axis.

        A positive value of sx skews the drawing right as y-axis values
        increase; a positive value of sy skews the drawing down as x-axis values
        increase.

        Mathematically, replaces :py:class:`Matrix` with a skew matrix
        premultiplied with :py:class:`Matrix`.

        This has the effect of skewing the drawing by (sx, sy) before
        transforming the result with :py:class:`Matrix`.

        :param float sx: amount to skew on x-axis
        :param float sy: amount to skew on y-axis
        )docstring",
        py::arg("sx"), py::arg("sy"))
    .def("concat", py::overload_cast<const SkMatrix&>(&SkCanvas::concat),
        R"docstring(
        Replaces :py:class:`Matrix` with matrix premultiplied with existing
        :py:class:`Matrix`.

        This has the effect of transforming the drawn geometry by matrix, before
        transforming the result with existing :py:class:`Matrix`.

        :matrix: matrix to premultiply with existing :py:class:`Matrix`
        )docstring",
        py::arg("matrix"))
    .def("concat", py::overload_cast<const SkM44&>(&SkCanvas::concat),
        py::arg("m44"))
    .def("setMatrix", &SkCanvas::setMatrix,
        R"docstring(
        Replaces :py:class:`Matrix` with matrix.

        Unlike :py:meth:`concat`, any prior matrix state is overwritten.

        :param skia.Matrix matrix: matrix to copy, replacing existing
            :py:class:`Matrix`
        )docstring",
        py::arg("matrix"))
    .def("resetMatrix", &SkCanvas::resetMatrix,
        R"docstring(
        Sets SkMatrix to the identity matrix.

        Any prior matrix state is overwritten.
        )docstring")
    .def("clipRect",
        py::overload_cast<const SkRect&, SkClipOp, bool>(&SkCanvas::clipRect),
        R"docstring(
        Replaces clip with the intersection or difference of clip and rect, with
        an aliased or anti-aliased clip edge.

        rect is transformed by :py:class:`Matrix` before it is combined with
        clip.

        :rect: :py:class:`Rect` to combine with clip
        :op: :py:class:`ClipOp` to apply to clip
        :doAntiAlias: true if clip is to be anti-aliased
        )docstring",
        py::arg("rect"), py::arg("op"), py::arg("doAntiAlias"))
    .def("clipRect",
        py::overload_cast<const SkRect&, SkClipOp>(&SkCanvas::clipRect),
        R"docstring(
        Replaces clip with the intersection or difference of clip and rect.

        Resulting clip is aliased; pixels are fully contained by the clip. rect
        is transformed by :py:class:`Matrix` before it is combined with clip.

        :rect: :py:class:`Rect` to combine with clip
        :op: :py:class:`ClipOp` to apply to clip
        )docstring",
        py::arg("rect"), py::arg("op"))
    .def("clipRect",
        py::overload_cast<const SkRect&, bool>(&SkCanvas::clipRect),
        R"docstring(
        Replaces clip with the intersection of clip and rect.

        Resulting clip is aliased; pixels are fully contained by the clip. rect
        is transformed by :py:class:`Matrix` before it is combined with clip.

        :rect: :py:class:`Rect` to combine with clip
        :doAntiAlias: true if clip is to be anti-aliased
        )docstring",
        py::arg("rect"), py::arg("doAntiAlias") = false)
    .def("androidFramework_setDeviceClipRestriction",
        &SkCanvas::androidFramework_setDeviceClipRestriction,
        R"docstring(
        Sets the maximum clip rectangle, which can be set by
        :py:meth:`clipRect`, :py:meth:`clipRRect` and :py:meth:`clipPath` and
        intersect the current clip with the specified rect.

        The maximum clip affects only future clipping operations; it is not
        retroactive. The clip restriction is not recorded in pictures.

        Pass an empty rect to disable maximum clip. This private API is for use
        by Android framework only.

        :param skia.IRect rect: maximum allowed clip in device coordinates
        )docstring",
        py::arg("rect"))
    .def("clipRRect",
        py::overload_cast<const SkRRect&, SkClipOp, bool>(&SkCanvas::clipRRect),
        R"docstring(
        Replaces clip with the intersection or difference of clip and rrect,
        with an aliased or anti-aliased clip edge.

        rrect is transformed by :py:class:`Matrix` before it is combined with
        clip.

        :rrect: :py:class:`RRect` to combine with clip
        :op: :py:class:`ClipOp` to apply to clip
        :doAntiAlias: true if clip is to be anti-aliased
        )docstring",
        py::arg("rrect"), py::arg("op"), py::arg("doAntiAlias"))
    .def("clipRRect",
        py::overload_cast<const SkRRect&, SkClipOp>(&SkCanvas::clipRRect),
        R"docstring(
        Replaces clip with the intersection or difference of clip and rrect.

        Resulting clip is aliased; pixels are fully contained by the clip. rrect
        is transformed by :py:class:`Matrix` before it is combined with clip.

        :rrect: :py:class:`RRect` to combine with clip
        :op: :py:class:`ClipOp` to apply to clip
        )docstring",
        py::arg("rrect"), py::arg("op"))
    .def("clipRRect",
        py::overload_cast<const SkRRect&, bool>(&SkCanvas::clipRRect),
        R"docstring(
        Replaces clip with the intersection of clip and rrect, with an aliased
        or anti-aliased clip edge.

        rrect is transformed by :py:class:`Matrix` before it is combined with
        clip.

        :rrect: :py:class:`RRect` to combine with clip
        :doAntiAlias: true if clip is to be anti-aliased
        )docstring",
        py::arg("rrect"), py::arg("doAntiAlias") = false)
    .def("clipPath",
        py::overload_cast<const SkPath&, SkClipOp, bool>(&SkCanvas::clipPath),
        R"docstring(
        Replaces clip with the intersection or difference of clip and path, with
        an aliased or anti-aliased clip edge.

        :py:class:`Path.FillType` determines if path describes the area inside
        or outside its contours; and if path contour overlaps itself or another
        path contour, whether the overlaps form part of the area. path is
        transformed by :py:class:`Matrix` before it is combined with clip.

        :path: :py:class:`Path` to combine with clip
        :op: :py:class:`ClipOp` to apply to clip
        :doAntiAlias: true if clip is to be anti-aliased
        )docstring",
        py::arg("path"), py::arg("op"), py::arg("doAntiAlias"))
    .def("clipPath",
        py::overload_cast<const SkPath&, SkClipOp>(&SkCanvas::clipPath),
        R"docstring(
        Replaces clip with the intersection or difference of clip and path.

        Resulting clip is aliased; pixels are fully contained by the clip.
        :py:class:`Path.FillType` determines if path describes the area inside
        or outside its contours; and if path contour overlaps itself or another
        path contour, whether the overlaps form part of the area. path is
        transformed by :py:class:`Matrix` before it is combined with clip.

        :path: :py:class:`Path` to combine with clip
        :op: :py:class:`ClipOp` to apply to clip
        )docstring",
        py::arg("path"), py::arg("op"))
    .def("clipPath",
        py::overload_cast<const SkPath&, bool>(&SkCanvas::clipPath),
        R"docstring(
        Replaces clip with the intersection of clip and path.

        Resulting clip is aliased; pixels are fully contained by the clip.
        :py:class:`Path.FillType` determines if path describes the area inside
        or outside its contours; and if path contour overlaps itself or another
        path contour, whether the overlaps form part of the area. path is
        transformed by :py:class:`Matrix` before it is combined with clip.

        :path: :py:class:`Path` to combine with clip
        :doAntiAlias: true if clip is to be anti-aliased
        )docstring",
        py::arg("path"), py::arg("doAntiAlias") = false)
    // .def("clipShader", &SkCanvas::clipShader)
    .def("clipRegion", &SkCanvas::clipRegion,
        R"docstring(
        Replaces clip with the intersection or difference of clip and
        :py:class:`Region` deviceRgn.

        Resulting clip is aliased; pixels are fully contained by the clip.
        deviceRgn is unaffected by :py:class:`Matrix`.

        :param skia.Region deviceRgn: :py:class:`Region` to combine with clip
        :param skia.ClipOp op: :py:class:`ClipOp` to apply to clip
        )docstring",
        py::arg("deviceRgn"), py::arg("op") = SkClipOp::kIntersect)
    .def("quickReject",
        py::overload_cast<const SkRect&>(&SkCanvas::quickReject, py::const_),
        R"docstring(
        Returns true if :py:class:`Rect` rect, transformed by
        :py:class:`Matrix`, can be quickly determined to be outside of clip.

        May return false even though rect is outside of clip.

        Use to check if an area to be drawn is clipped out, to skip subsequent
        draw calls.

        :rect: :py:class:`Rect` to compare with clip
        :return: true if rect, transformed by :py:class:`Matrix`, does not
            intersect clip
        )docstring",
        py::arg("rect"))
    .def("quickReject",
        py::overload_cast<const SkPath&>(&SkCanvas::quickReject, py::const_),
        R"docstring(
        Returns true if path, transformed by :py:class:`Matrix`, can be
        quickly determined to be outside of clip.

        May return false even though path is outside of clip.

        Use to check if an area to be drawn is clipped out, to skip subsequent
        draw calls.

        :path: :py:class:`Path` to compare with clip
        :return: true if path, transformed by :py:class:`Matrix`, does not
            intersect clip
        )docstring",
        py::arg("path"))
    .def("getLocalClipBounds",
        py::overload_cast<>(&SkCanvas::getLocalClipBounds, py::const_),
        R"docstring(
        Returns bounds of clip, transformed by inverse of :py:class:`Matrix`.

        If clip is empty, return :py:meth:`Rect.MakeEmpty`, where all
        :py:class:`Rect` sides equal zero.

        :py:class:`Rect` returned is outset by one to account for partial pixel
        coverage if clip is anti-aliased.

        :return: bounds of clip in local coordinates
        )docstring")
    .def("getLocalClipBounds",
        py::overload_cast<SkRect*>(&SkCanvas::getLocalClipBounds, py::const_),
        R"docstring(
        Returns bounds of clip, transformed by inverse of :py:class:`Matrix`.

        If clip is empty, return false, and set bounds to
        :py:meth:`Rect.MakeEmpty`, where all :py:class:`Rect` sides equal zero.

        bounds is outset by one to account for partial pixel coverage if clip is
        anti-aliased.

        :bounds: :py:class:`Rect` of clip in local coordinates
        :return: true if clip bounds is not empty
        )docstring",
        py::arg("bounds"))
    .def("getDeviceClipBounds",
        py::overload_cast<>(&SkCanvas::getDeviceClipBounds, py::const_),
        R"docstring(
        Returns :py:class:`IRect` bounds of clip, unaffected by
        :py:class:`Matrix`.

        If clip is empty, return :py:meth:`Rect.MakeEmpty`, where all
        :py:class:`Rect` sides equal zero.

        Unlike :py:meth:`getLocalClipBounds`, returned :py:class:`IRect` is not
        outset.

        :return: bounds of clip in :py:class:`BaseDevice` coordinates
        )docstring")
    .def("getDeviceClipBounds",
        py::overload_cast<SkIRect*>(&SkCanvas::getDeviceClipBounds, py::const_),
        R"docstring(
        Returns :py:class:`IRect` bounds of clip, unaffected by
        :py:class:`Matrix`.

        If clip is empty, return false, and set bounds to
        :py:meth:`Rect.MakeEmpty`, where all :py:class:`Rect` sides equal zero.

        Unlike :py:meth:`getLocalClipBounds`, returned :py:class:`IRect` is not
        outset.

        :param skia.Rect bounds: :py:class:`Rect` of clip in device coordinates
        :return: bounds of clip in :py:class:`BaseDevice` coordinates
        )docstring",
        py::arg("bounds"))
    .def("drawColor",
        py::overload_cast<SkColor, SkBlendMode>(&SkCanvas::drawColor),
        R"docstring(
        Fills clip with color color.

        mode determines how ARGB is combined with destination.

        :param int color: unpremultiplied ARGB
        :param skia.BlendMode mode: :py:class:`BlendMode` used to combine source
            color and destination
        )docstring",
        py::arg("color"), py::arg("mode") = SkBlendMode::kSrcOver)
    .def("drawColor",
        py::overload_cast<const SkColor4f&, SkBlendMode>(&SkCanvas::drawColor),
        R"docstring(
        Fills clip with color color.

        mode determines how ARGB is combined with destination.

        :param color: :py:class:`Color4f` representing unpremultiplied color.
        :param skia.BlendMode mode: :py:class:`BlendMode` used to combine source
            color and destination
        )docstring",
        py::arg("color"), py::arg("mode") = SkBlendMode::kSrcOver)
    .def("clear",
        py::overload_cast<SkColor>(&SkCanvas::clear),
        R"docstring(
        Fills clip with color color using :py:attr:`BlendMode.kSrc`.

        This has the effect of replacing all pixels contained by clip with
        color.

        :param int color: unpremultiplied ARGB
        )docstring",
        py::arg("color"))
    .def("clear",
        py::overload_cast<const SkColor4f&>(&SkCanvas::clear),
        R"docstring(
        Fills clip with color color using :py:attr:`BlendMode.kSrc`.

        This has the effect of replacing all pixels contained by clip with
        color.

        :param color: :py:class:`Color4f` representing unpremultiplied color.
        )docstring",
        py::arg("color"))
    .def("discard", &SkCanvas::discard,
        R"docstring(
        Makes :py:class:`Canvas` contents undefined.

        Subsequent calls that read :py:class:`Canvas` pixels, such as drawing
        with :py:class:`BlendMode`, return undefined results. :py:meth:`discard`
        does not change clip or :py:class:`Matrix`.

        :py:meth:`discard` may do nothing, depending on the implementation of
        :py:class:`Surface` or :py:class:`BaseDevice` that created
        :py:class:`Canvas`.

        :py:meth:`discard` allows optimized performance on subsequent draws by
        removing cached data associated with :py:class:`Surface` or
        :py:class:`BaseDevice`. It is not necessary to call :py:meth:`discard`
        once done with SkCanvas; any cached data is deleted when owning
        :py:class:`Surface` or :py:class:`BaseDevice` is deleted.
        )docstring")
    .def("drawPaint", &SkCanvas::drawPaint,
        R"docstring(
        Fills clip with :py:class:`Paint` paint.

        :py:class:`Paint` components :py:class:`MaskFilter`, :py:class:`Shader`,
        :py:class:`ColorFilter`, :py:class:`ImageFilter`, and
        :py:class:`BlendMode` affect drawing; :py:class:`PathEffect` in paint is
        ignored.

        :param skia.Paint paint: graphics state used to fill :py:class:`Canvas`
        )docstring",
        py::arg("paint"))
    .def("drawPoints",
        // &SkCanvas::drawPoints,
        [] (SkCanvas& canvas, SkCanvas::PointMode mode,
            const std::vector<SkPoint>& points, const SkPaint &paint) {
            canvas.drawPoints(mode, points.size(), &points[0], paint);
        },
        R"docstring(
        Draws pts using clip, :py:class:`Matrix` and :py:class:`Paint`
        paint.

        mode may be one of: :py:attr:`~Canvas.PointMode.kPoints`,
        :py:attr:`~Canvas.PointMode.kLines`, or
        :py:attr:`~Canvas.PointMode.kPolygon`.

        If mode is :py:attr:`kPoints`, the shape of point drawn depends on
        paint :py:class:`Paint.Cap`. If paint is set to :py:attr:`Paint.kRound`,
        each point draws a circle of diameter :py:class:`Paint` stroke width. If
        paint is set to :py:attr:`Paint.kSquare` or :py:attr:`Paint.kButt`, each
        point draws a square of width and height :py:attr:`Paint` stroke width.

        If mode is :py:attr:`kLines`, each pair of points draws a line segment.
        One line is drawn for every two points; each point is used once. If
        count is odd, the final point is ignored.

        If mode is :py:attr:`kPolygon`, each adjacent pair of points draws a
        line segment. count minus one lines are drawn; the first and last point
        are used once.

        Each line segment respects paint :py:class:`Paint.Cap` and
        :py:class:`Paint` stroke width. :py:class:`Paint.Style` is ignored, as
        if were set to :py:attr:`Paint.kStroke`.

        Always draws each element one at a time; is not affected by
        :py:class:`Paint.Join`, and unlike :py:meth:`drawPath`, does not create
        a mask from all points and lines before drawing.

        :param skia.Canvas.PointMode mode: whether pts draws points or lines
        :param Iterable[skia.Point] pts: array of points to draw
        :param skia.Paint paint: stroke, blend, color, and so on, used to draw
        )docstring",
        py::arg("mode"), py::arg("pts"), py::arg("paint"))
    .def("drawPoint",
        py::overload_cast<SkScalar, SkScalar, const SkPaint&>(
            &SkCanvas::drawPoint),
        R"docstring(
        Draws point at (x, y) using clip, :py:class:`Matrix` and
        :py:class:`Paint` paint.

        The shape of point drawn depends on paint :py:class:`Paint.Cap`. If
        paint is set to :py:attr:`Paint.Cap.kRound`, draw a circle of diameter
        :py:class:`Paint` stroke width. If paint is set to
        :py:attr:`Paint.Cap.kSquare` or :py:class:`Paint.Cap.Butt`, draw a
        square of width and height :py:class:`Paint` stroke width.
        :py:class:`Paint.Style` is ignored, as if were set to
        :py:class:`Paint.Style.kStroke`.

        :x: left edge of circle or square
        :y: top edge of circle or square
        :paint: stroke, blend, color, and so on, used to draw
        )docstring",
        py::arg("x"), py::arg("y"), py::arg("paint"))
    .def("drawPoint",
        py::overload_cast<SkPoint, const SkPaint&>(&SkCanvas::drawPoint),
        R"docstring(
        Draws point p using clip, :py:class:`Matrix` and :py:class:`Paint`
        paint.

        The shape of point drawn depends on paint :py:class:`Paint.Cap`. If
        paint is set to :py:attr:`Paint.Cap.kRound`, draw a circle of diameter
        :py:class:`Paint` stroke width. If paint is set to
        :py:attr:`Paint.Cap.kSquare` or :py:class:`Paint.Cap.Butt`, draw a
        square of width and height :py:class:`Paint` stroke width.
        :py:class:`Paint.Style` is ignored, as if were set to
        :py:class:`Paint.Style.kStroke`.

        :p: top-left edge of circle or square
        :paint: stroke, blend, color, and so on, used to draw
        )docstring",
        py::arg("p"), py::arg("paint"))
    .def("drawLine",
        py::overload_cast<SkScalar, SkScalar, SkScalar, SkScalar,
            const SkPaint&>(&SkCanvas::drawLine),
        R"docstring(
        Draws line segment from (x0, y0) to (x1, y1) using clip,
        :py:class:`Matrix`, and :py:class:`Paint` paint.

        In paint: :py:class:`Paint` stroke width describes the line thickness;
        :py:class:`Paint.Cap` draws the end rounded or square;
        :py:class:`Paint.Style` is ignored, as if were set to
        :py:attr:`Paint.Style.kStroke`.

        :x0: start of line segment on x-axis
        :y0: start of line segment on y-axis
        :x1: end of line segment on x-axis
        :y1: end of line segment on y-axis
        :paint: stroke, blend, color, and so on, used to draw
        )docstring",
        py::arg("x0"), py::arg("y0"), py::arg("x1"), py::arg("y1`"),
        py::arg("paint"))
    .def("drawLine",
        py::overload_cast<SkPoint, SkPoint, const SkPaint&>(
            &SkCanvas::drawLine),
        R"docstring(
        Draws line segment from p0 to p1 using clip, :py:class:`Matrix`, and
        :py:class:`Paint` paint.

        In paint: :py:class:`Paint` stroke width describes the line thickness;
        :py:class:`Paint.Cap` draws the end rounded or square;
        :py:class:`Paint.Style` is ignored, as if were set to
        :py:attr:`Paint.Style.kStroke`.

        :p0: start of line segment
        :p1: end of line segment
        :paint: stroke, blend, color, and so on, used to draw
        )docstring",
        py::arg("p0"), py::arg("p1"), py::arg("paint"))
    .def("drawRect", &SkCanvas::drawRect,
        R"docstring(
        Draws :py:class:`Rect` rect using clip, :py:class:`Matrix`, and
        :py:class:`Paint` paint.

        In paint: :py:class:`Paint.Style` determines if rectangle is stroked or
        filled; if stroked, :py:class:`Paint` stroke width describes the line
        thickness, and :py:class:`Paint.Join` draws the corners rounded or
        square.

        :param skia.Rect rect: rectangle to draw
        :param skia.Paint paint: stroke or fill, blend, color, and so on, used
            to draw
        )docstring",
        py::arg("rect"), py::arg("paint"))
    .def("drawIRect", &SkCanvas::drawIRect,
        R"docstring(
        Draws :py:class:`IRect` rect using clip, :py:class:`Matrix`, and
        :py:class:`Paint` paint.

        In paint: :py:class:`Paint.Style` determines if rectangle is stroked or
        filled; if stroked, :py:class:`Paint` stroke width describes the line
        thickness, and :py:class:`Paint.Join` draws the corners rounded or
        square.

        :param skia.IRect rect: rectangle to draw
        :param skia.Paint paint: stroke or fill, blend, color, and so on, used
            to draw
        )docstring",
        py::arg("rect"), py::arg("paint"))
    .def("drawRegion", &SkCanvas::drawRegion,
        R"docstring(
        Draws :py:class:`Region` region using clip, :py:class:`Matrix`, and
        :py:class:`Paint` paint.

        In paint: :py:class:`Paint.Style` determines if rectangle is stroked or
        filled; if stroked, :py:class:`Paint` stroke width describes the line
        thickness, and :py:class:`Paint.Join` draws the corners rounded or
        square.

        :param skia.Region region: region to draw
        :param skia.Paint paint: stroke or fill, blend, color, and so on, used
            to draw
        )docstring",
        py::arg("region"), py::arg("paint"))
    .def("drawOval", &SkCanvas::drawOval,
        R"docstring(
        Draws oval using clip, :py:class:`Matrix`, and :py:class:`Paint` paint.

        In paint: :py:class:`Paint.Style` determines if oval is stroked or
        filled; if stroked, :py:class:`Paint` stroke width describes the line
        thickness, and :py:class:`Paint.Join` draws the corners rounded or
        square.

        :param skia.Rect oval: oval to draw
        :param skia.Paint paint: stroke or fill, blend, color, and so on, used
            to draw
        )docstring",
        py::arg("oval"), py::arg("paint"))
    .def("drawRRect", &SkCanvas::drawRRect,
        R"docstring(
        Draws :py:class:`RRect` rrect using clip, :py:class:`Matrix`, and
        :py:class:`Paint` paint.

        In paint: :py:class:`Paint.Style` determines if rrect is stroked or
        filled; if stroked, :py:class:`Paint` stroke width describes the line
        thickness, and :py:class:`Paint.Join` draws the corners rounded or
        square.

        rrect may represent a rectangle, circle, oval, uniformly rounded
        rectangle, or may have any combination of positive non-square radii for
        the four corners.

        :param skia.RRect rrect: rrect to draw
        :param skia.Paint paint: stroke or fill, blend, color, and so on, used
            to draw
        )docstring",
        py::arg("rrect"), py::arg("paint"))
    .def("drawDRRect", &SkCanvas::drawDRRect,
        R"docstring(
        Draws :py:class:`RRect` outer and inner using clip, :py:class:`Matrix`,
        and :py:class:`Paint` paint.

        outer must contain inner or the drawing is undefined. In paint:
        :py:class:`Paint.Style` determines if :py:class:`RRect` is stroked or
        filled; if stroked, :py:class:`Paint` stroke width describes the line
        thickness, and :py:class:`Paint.Join` draws the corners rounded or
        square.

        GPU-backed platforms optimize drawing when both outer and inner are
        concave and outer contains inner. These platforms may not be able to
        draw :py:class:`Path` built with identical data as fast.

        :param skia.RRect outer: :py:class:`RRect` outer bounds to draw
        :param skia.RRect inner: :py:class:`RRect` inner bounds to draw
        :param skia.Paint paint: stroke or fill, blend, color, and so on, used
            to draw
        )docstring",
        py::arg("outer"), py::arg("inner"), py::arg("paint"))
    .def("drawCircle",
        py::overload_cast<SkScalar, SkScalar, SkScalar, const SkPaint&>(
            &SkCanvas::drawCircle),
        R"docstring(
        Draws circle at (cx, cy) with radius using clip, :py:class:`Matrix`, and
        :py:class:`Paint` paint.

        If radius is zero or less, nothing is drawn. In paint:
        :py:class:`Paint.Style` determines if circle is stroked or filled; if
        stroked, :py:class:`Paint` stroke width describes the line thickness.

        :cx: circle center on the x-axis
        :cy: circle center on the y-axis
        :radius: half the diameter of circle
        :paint: :py:class:`Paint` stroke or fill, blend, color, and so on, used
            to draw
        )docstring",
        py::arg("cx"), py::arg("cy"), py::arg("radius"), py::arg("paint"))
    .def("drawCircle",
        py::overload_cast<SkPoint, SkScalar, const SkPaint&>(
            &SkCanvas::drawCircle),
        R"docstring(
        Draws circle at center with radius using clip, :py:class:`Matrix`, and
        :py:class:`Paint` paint.

        If radius is zero or less, nothing is drawn. In paint:
        :py:class:`Paint.Style` determines if circle is stroked or filled; if
        stroked, :py:class:`Paint` stroke width describes the line thickness.

        :center: circle center
        :radius: half the diameter of circle
        :paint: :py:class:`Paint` stroke or fill, blend, color, and so on, used
            to draw
        )docstring",
        py::arg("center"), py::arg("radius"), py::arg("paint"))
    .def("drawArc", &SkCanvas::drawArc,
        R"docstring(
        Draws arc using clip, :py:class:`Matrix`, and :py:class:`Paint` paint.

        Arc is part of oval bounded by oval, sweeping from startAngle to
        startAngle plus sweepAngle. startAngle and sweepAngle are in degrees.

        startAngle of zero places start point at the right middle edge of oval.
        A positive sweepAngle places arc end point clockwise from start point; a
        negative sweepAngle places arc end point counterclockwise from start
        point. sweepAngle may exceed 360 degrees, a full circle. If useCenter is
        true, draw a wedge that includes lines from oval center to arc end
        points. If useCenter is false, draw arc between end points.

        If :py:class:`Rect` oval is empty or sweepAngle is zero, nothing is
        drawn.

        :param skia.Rect oval: :py:class:`Rect` bounds of oval containing arc to
            draw
        :param float startAngle: angle in degrees where arc begins
        :param float sweepAngle: sweep angle in degrees; positive is clockwise
        :param bool useCenter: if true, include the center of the oval
        :param skia.Paint paint: :py:class:`Paint` stroke or fill, blend, color,
            and so on, used to draw
        )docstring",
        py::arg("oval"), py::arg("startAngle"), py::arg("sweepAngle"),
        py::arg("useCenter"), py::arg("paint"))
    .def("drawRoundRect", &SkCanvas::drawRoundRect,
        R"docstring(
        Draws :py:class:`RRect` bounded by :py:class:`Rect` rect, with corner
        radii (rx, ry) using clip, :py:class:`Matrix`, and :py:class:`Paint`
        paint.

        In paint: :py:class:`Paint.Style` determines if :py:class:`RRect` is
        stroked or filled; if stroked, :py:class:`Paint` stroke width describes
        the line thickness. If rx or ry are less than zero, they are treated as
        if they are zero. If rx plus ry exceeds rect width or rect height, radii
        are scaled down to fit. If rx and ry are zero, :py:class:`RRect` is
        drawn as :py:class:`Rect` and if stroked is affected by
        :py:class:`Paint.Join`.

        :param skia.Rect rect: SkRect bounds of SkRRect to draw
        :param float rx: axis length on x-axis of oval describing rounded
            corners
        :param float ry: axis length on y-axis of oval describing rounded
            corners
        :param skia.Paint paint: stroke, blend, color, and so on, used to draw
        )docstring",
        py::arg("rect"), py::arg("rx"), py::arg("ry"), py::arg("paint"))
    .def("drawPath", &SkCanvas::drawPath,
        R"docstring(
        Draws :py:class:`Path` path using clip, :py:class:`Matrix`, and
        :py:class:`Paint` paint.

        :py:class:`Path` contains an array of path contour, each of which may be
        open or closed.

        In paint: :py:class:`Paint.Style` determines if :py:class:`RRect` is
        stroked or filled: if filled, :py:class:`Path.FillType` determines
        whether path contour describes inside or outside of fill; if stroked,
        :py:class:`Paint` stroke width describes the line thickness,
        :py:class:`Paint`::Cap describes line ends, and :py:class:`Paint.Join`
        describes how corners are drawn.

        :param skia.Path path: :py:class:`Path` to draw
        :param skia.Paint paint: stroke, blend, color, and so on, used to draw
        )docstring",
        py::arg("path"), py::arg("paint"))
    .def("drawImage",
        py::overload_cast<const SkImage*, SkScalar, SkScalar,
            const SkPaint*>(&SkCanvas::drawImage),
        R"docstring(
        Draws :py:class:`Image` image, with its top-left corner at (left, top),
        using clip, :py:class:`Matrix`, and optional :py:class:`Paint` paint.

        This is equivalent to drawImageRect() using a dst rect at (x,y) with the
        same width and height of the image.

        :image: uncompressed rectangular map of pixels
        :left: left side of image
        :top: top side of image
        :paint: :py:class:`Paint` containing :py:class:`BlendMode`,
            :py:class:`ColorFilter`, :py:class:`ImageFilter`, and so on; or
            nullptr
        )docstring",
        py::arg("image"), py::arg("left"), py::arg("top"),
        py::arg("paint") = nullptr)
    // .def("drawImage",
    //     py::overload_cast<const sk_sp<SkImage>&, SkScalar, SkScalar,
    //         const SkPaint*>(&SkCanvas::drawImage),
    //     py::arg("image"), py::arg("left"), py::arg("top"),
    //     py::arg("paint") = nullptr)
    .def("drawImageRect",
        py::overload_cast<const SkImage*, const SkRect&, const SkRect&,
            const SkPaint*, SkCanvas::SrcRectConstraint>(
                &SkCanvas::drawImageRect),
        R"docstring(
        Draws :py:class:`Rect` src of :py:class:`Image` image, scaled and
        translated to fill :py:class:`Rect` dst.

        Additionally transform draw using clip, :py:class:`Matrix`, and optional
        :py:class:`Paint` paint.

        If :py:class:`Paint` paint is supplied, apply :py:class:`ColorFilter`,
        alpha, :py:class:`ImageFilter`, :py:class:`BlendMode`, and
        :py:class:`DrawLooper`. If image is
        :py:attr:`ColorType.kAlpha_8_ColorType`, apply :py:class:`Shader`. If
        paint contains :py:class:`MaskFilter`, generate mask from image bounds.

        If generated mask extends beyond image bounds, replicate image edge
        colors, just as :py:class:`Shader` made from
        :py:meth:`Image.makeShader` with :py:attr:`TileMode.kClamp`
        set replicates the image edge color when it samples outside of its
        bounds.

        When using a shader or shader mask filter, its coordinate system is
        based on the current CTM, so will reflect the dst rect geometry and is
        equivalent to drawRect(dst). The src rect is only used to access the
        provided image.

        constraint set to :py:attr:`kStrict_SrcRectConstraint` limits
        :py:class:`Paint` :py:class:`FilterQuality` to sample within src; set to
        :py:attr:`kFast_SrcRectConstraint` allows sampling outside to improve
        performance.

        :image: :py:class:`Image` containing pixels, dimensions, and format
        :src: source :py:class:`Rect` of image to draw from
        :dst: destination :py:class:`Rect` of image to draw to
        :paint: :py:class:`Paint` containing :py:class:`BlendMode`,
            :py:class:`ColorFilter`, :py:class:`ImageFilter`, and so on; or
            nullptr
        :constraint: filter strictly within src or draw faster
        )docstring",
        py::arg("image"), py::arg("src"), py::arg("dst"),
        py::arg("paint") = nullptr, py::arg("constraint") =
            SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint)
    .def("drawImageRect",
        py::overload_cast<const SkImage*, const SkIRect&, const SkRect&,
            const SkPaint*, SkCanvas::SrcRectConstraint>(
            &SkCanvas::drawImageRect),
        R"docstring(
        Draws :py:class:`IRect` isrc of :py:class:`Image` image, scaled and
        translated to fill :py:class:`Rect` dst.

        Additionally transform draw using clip, :py:class:`Matrix`, and optional
        :py:class:`Paint` paint.

        If :py:class:`Paint` paint is supplied, apply :py:class:`ColorFilter`,
        alpha, :py:class:`ImageFilter`, :py:class:`BlendMode`, and
        :py:class:`DrawLooper`. If image is
        :py:attr:`ColorType.kAlpha_8_ColorType`, apply :py:class:`Shader`. If
        paint contains :py:class:`MaskFilter`, generate mask from image bounds.

        If generated mask extends beyond image bounds, replicate image edge
        colors, just as :py:class:`Shader` made from
        :py:meth:`Image.makeShader` with :py:attr:`TileMode.kClamp`
        set replicates the image edge color when it samples outside of its
        bounds.

        When using a shader or shader mask filter, its coordinate system is
        based on the current CTM, so will reflect the dst rect geometry and is
        equivalent to drawRect(dst). The isrc rect is only used to access the
        provided image.

        constraint set to :py:attr:`kStrict_SrcRectConstraint` limits
        :py:class:`Paint` :py:class:`FilterQuality` to sample within isrc; set
        to :py:attr:`kFast_SrcRectConstraint` allows sampling outside to improve
        performance.

        :image: :py:class:`Image` containing pixels, dimensions, and format
        :isrc: source :py:class:`IRect` of image to draw from
        :dst: destination :py:class:`Rect` of image to draw to
        :paint: :py:class:`Paint` containing :py:class:`BlendMode`,
            :py:class:`ColorFilter`, :py:class:`ImageFilter`, and so on; or
            nullptr
        :constraint: filter strictly within isrc or draw faster
        )docstring",
        py::arg("image"), py::arg("isrc"), py::arg("dst"),
        py::arg("paint") = nullptr, py::arg("constraint") =
            SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint)
    .def("drawImageRect",
        py::overload_cast<const SkImage*, const SkRect&, const SkPaint*>(
            &SkCanvas::drawImageRect),
        R"docstring(
        Draws :py:class:`Image` image, scaled and translated to fill
        :py:class:`Rect` dst, using clip, :py:class:`Matrix`, and optional
        :py:class:`Paint` paint.

        If :py:class:`Paint` paint is supplied, apply :py:class:`ColorFilter`,
        alpha, :py:class:`ImageFilter`, :py:class:`BlendMode`, and
        :py:class:`DrawLooper`. If image is
        :py:attr:`ColorType.kAlpha_8_ColorType`, apply :py:class:`Shader`. If
        paint contains :py:class:`MaskFilter`, generate mask from image bounds.

        If generated mask extends beyond image bounds, replicate image edge
        colors, just as :py:class:`Shader` made from
        :py:meth:`Image.makeShader` with :py:attr:`TileMode.kClamp`
        set replicates the image edge color when it samples outside of its
        bounds.

        When using a shader or shader mask filter, its coordinate system is
        based on the current CTM, so will reflect the dst rect geometry and is
        equivalent to drawRect(dst).

        :image: :py:class:`Image` containing pixels, dimensions, and format
        :dst: destination :py:class:`Rect` of image to draw to
        :paint: :py:class:`Paint` containing :py:class:`BlendMode`,
            :py:class:`ColorFilter`, :py:class:`ImageFilter`, and so on; or
            nullptr
        :constraint: filter strictly within src or draw faster
        )docstring",
        py::arg("image"), py::arg("dst"), py::arg("paint") = nullptr)
    // .def("drawImageRect",
    //     py::overload_cast<const sk_sp<SkImage>&, const SkRect&, const SkRect&,
    //         const SkPaint*, SkCanvas::SrcRectConstraint>(
    //         &SkCanvas::drawImageRect),
    //     "Draws SkRect src of SkImage image, scaled and translated to fill "
    //     "SkRect dst.",
    //     py::arg("image"), py::arg("src"), py::arg("dst"), py::arg("paint"),
    //     py::arg("constraint") =
    //         SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint)
    // .def("drawImageRect",
    //     py::overload_cast<const sk_sp<SkImage>&, const SkIRect&,
    //         const SkRect&, const SkPaint*, SkCanvas::SrcRectConstraint))
    //     &SkCanvas::drawImageRect,
    //     "Draws SkIRect isrc of SkImage image, scaled and translated to fill "
    //     "SkRect dst.",
    //     py::arg("image"), py::arg("isrc"), py::arg("dst"), py::arg("paint"),
    //     py::arg("constraint") =
    //         SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint)
    // .def("drawImageRect",
    //     py::overload_cast<const sk_sp<SkImage>&, const SkRect&,
    //         const SkPaint*>(&SkCanvas::drawImageRect),
    //     "Draws SkImage image, scaled and translated to fill SkRect dst, using "
    //     "clip, SkMatrix, and optional SkPaint paint.")
    .def("drawImageNine",
        py::overload_cast<const SkImage*, const SkIRect&, const SkRect&,
            const SkPaint*>(&SkCanvas::drawImageNine),
        R"docstring(
        Draws :py:class:`Image` image stretched proportionally to fit into
        :py:class:`Rect` dst.

        :py:class:`IRect` center divides the image into nine sections: four
        sides, four corners, and the center. Corners are unmodified or scaled
        down proportionately if their sides are larger than dst; center and four
        sides are scaled to fit remaining space, if any.

        Additionally transform draw using clip, :py:class:`Matrix`, and optional
        :py:class:`Paint` paint.

        If :py:class:`Paint` paint is supplied, apply :py:class:`ColorFilter`,
        alpha, :py:class:`ImageFilter`, :py:class:`BlendMode`, and
        :py:class:`DrawLooper`. If image is
        :py:attr:`ColorType.kAlpha_8_ColorType`, apply :py:class:`Shader`. If
        paint contains :py:class:`MaskFilter`, generate mask from image bounds.
        If paint :py:class:`FilterQuality` set to
        :py:attr:`FilterQuality.kNone`, disable pixel filtering. For all other
        values of paint :py:class:`FilterQuality`, use
        :py:attr:`FilterQuality.kLow` to filter pixels. Any
        :py:class:`MaskFilter` on paint is ignored as is paint anti-aliasing
        state.

        If generated mask extends beyond image bounds, replicate image edge
        colors, just as :py:class:`Shader` made from :py:meth:`Image.makeShader`
        with :py:attr:`TileMode.kClamp` set replicates the image edge color when
        it samples outside of its bounds.

        :param skia.Image image: :py:class:`Image` containing pixels,
            dimensions, and format
        :param skia.IRect center: :py:class:`IRect` edge of image corners and
            sides
        :param skia.Rect dst: destination :py:class:`Rect` of image to draw to
        :param skia.Paint paint: :py:class:`Paint` containing
            :py:class:`BlendMode`, :py:class:`ColorFilter`,
            :py:class:`ImageFilter`, and so on; or nullptr
        )docstring",
        py::arg("image"), py::arg("center"), py::arg("dst"),
        py::arg("paint") = nullptr)
    // .def("drawImageNine",
    //     py::overload_cast<const sk_sp<SkImage>&, const SkIRect&,
    //         const SkRect&, const SkPaint*>(&SkCanvas::drawImageNine))
    .def("drawBitmap", &SkCanvas::drawBitmap,
        R"docstring(
        Draws :py:class:`Bitmap` bitmap, with its top-left corner at (left,
        top), using clip, :py:class:`Matrix`, and optional :py:class:`Paint`
        paint.

        If :py:class:`Paint` paint is not nullptr, apply
        :py:class:`ColorFilter`, alpha, :py:class:`ImageFilter`,
        :py:class:`BlendMode`, and :py:class:`DrawLooper`. If bitmap is
        :py:attr:`ColorType.kAlpha_8_ColorType`, apply :py:class:`Shader`. If
        paint contains :py:class:`MaskFilter`, generate mask from bitmap bounds.

        If generated mask extends beyond bitmap bounds, replicate bitmap edge
        colors, just as :py:class:`Shader` made from
        :py:meth:`Shader.MakeBitmapShader` with :py:attr:`TileMode.kClamp` set
        replicates the bitmap edge color when it samples outside of its bounds.

        :param skia.Bitmap bitmap:  :py:class:`Bitmap` containing pixels,
            dimensions, and format
        :param left: left side of bitmap
        :param top: top side of bitmap
        :param paint: :py:class:`Paint` containing :py:class:`BlendMode`,
            :py:class:`ColorFilter`, :py:class:`ImageFilter`, and so on; or
            nullptr
        )docstring",
        py::arg("bitmap"), py::arg("left"), py::arg("top"),
        py::arg("paint") = nullptr)
    .def("drawBitmapRect",
        py::overload_cast<const SkBitmap&, const SkRect&, const SkRect&,
            const SkPaint*, SkCanvas::SrcRectConstraint>(
                &SkCanvas::drawBitmapRect),
        R"docstring(
        Draws :py:class:`Rect` src of :py:class:`Bitmap` bitmap, scaled and
        translated to fill :py:class:`Rect` dst.

        Additionally transform draw using clip, :py:class:`Matrix`, and optional
        :py:class:`Paint` paint.

        If :py:class:`Paint` paint is supplied, apply :py:class:`ColorFilter`,
        alpha, :py:class:`ImageFilter`, :py:class:`BlendMode`,
        and :py:class:`DrawLooper`. If bitmap is
        :py:attr:`ColorType.kAlpha_8_ColorType`, apply :py:class:`Shader`. If
        paint contains :py:class:`MaskFilter`, generate mask from bitmap bounds.

        If generated mask extends beyond bitmap bounds, replicate bitmap edge
        colors, just as :py:class:`Shader` made from
        :py:meth:`Shader.MakeBitmapShader` with :py:attr:`TileMode.kClamp` set
        replicates the bitmap edge color when it samples outside of its bounds.

        constraint set to :py:attr:`kStrict_SrcRectConstraint` limits
        :py:class:`Paint` :py:class:`FilterQuality` to sample within src; set to
        :py:attr:`kFast_SrcRectConstraint` allows sampling outside to improve
        performance.

        :bitmap: :py:class:`Bitmap` containing pixels, dimensions, and format
        :src: source :py:class:`Rect` of image to draw from
        :dst: destination :py:class:`Rect` of image to draw to
        :paint: :py:class:`Paint` containing :py:class:`BlendMode`,
            :py:class:`ColorFilter`, :py:class:`ImageFilter`, and so on; or
            nullptr
        :constraint: filter strictly within src or draw faster
        )docstring",
        py::arg("bitmap"), py::arg("src"), py::arg("dst"),
        py::arg("paint") = nullptr, py::arg("constraint") =
            SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint)
    .def("drawBitmapRect",
        py::overload_cast<const SkBitmap&, const SkIRect&, const SkRect&,
            const SkPaint*, SkCanvas::SrcRectConstraint>(
                &SkCanvas::drawBitmapRect),
        R"docstring(
        Draws :py:class:`IRect` isrc of :py:class:`Bitmap` bitmap, scaled and
        translated to fill :py:class:`Rect` dst.

        Additionally transform draw using clip, :py:class:`Matrix`, and optional
        :py:class:`Paint` paint.

        If :py:class:`Paint` paint is supplied, apply :py:class:`ColorFilter`,
        alpha, :py:class:`ImageFilter`, :py:class:`BlendMode`,
        and :py:class:`DrawLooper`. If bitmap is
        :py:attr:`ColorType.kAlpha_8_ColorType`, apply :py:class:`Shader`. If
        paint contains :py:class:`MaskFilter`, generate mask from bitmap bounds.

        If generated mask extends beyond bitmap bounds, replicate bitmap edge
        colors, just as :py:class:`Shader` made from
        :py:meth:`Shader.MakeBitmapShader` with :py:attr:`TileMode.kClamp` set
        replicates the bitmap edge color when it samples outside of its bounds.

        constraint set to :py:attr:`kStrict_SrcRectConstraint` limits
        :py:class:`Paint` :py:class:`FilterQuality` to sample within isrc; set
        to :py:attr:`kFast_SrcRectConstraint` allows sampling outside to improve
        performance.

        :bitmap: :py:class:`Bitmap` containing pixels, dimensions, and format
        :isrc: source :py:class:`IRect` of image to draw from
        :dst: destination :py:class:`Rect` of image to draw to
        :paint: :py:class:`Paint` containing :py:class:`BlendMode`,
            :py:class:`ColorFilter`, :py:class:`ImageFilter`, and so on; or
            nullptr
        :constraint: filter strictly within isrc or draw faster
        )docstring",
        py::arg("bitmap"), py::arg("isrc"), py::arg("dst"),
        py::arg("paint") = nullptr, py::arg("constraint") =
            SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint)
    .def("drawBitmapRect",
        py::overload_cast<const SkBitmap&, const SkRect&, const SkPaint*,
            SkCanvas::SrcRectConstraint>(&SkCanvas::drawBitmapRect),
        R"docstring(
        Draws :py:class:`Bitmap` bitmap, scaled and translated to fill
        :py:class:`Rect` dst.

        bitmap bounds is on integer pixel boundaries; dst may include fractional
        boundaries. Additionally transform draw using clip, :py:class:`Matrix`,
        and optional :py:class:`Paint` paint.

        If :py:class:`Paint` paint is supplied, apply :py:class:`ColorFilter`,
        alpha, :py:class:`ImageFilter`, :py:class:`BlendMode`,
        and :py:class:`DrawLooper`. If bitmap is
        :py:attr:`ColorType.kAlpha_8_ColorType`, apply :py:class:`Shader`. If
        paint contains :py:class:`MaskFilter`, generate mask from bitmap bounds.

        If generated mask extends beyond bitmap bounds, replicate bitmap edge
        colors, just as :py:class:`Shader` made from
        :py:meth:`Shader.MakeBitmapShader` with :py:attr:`TileMode.kClamp` set
        replicates the bitmap edge color when it samples outside of its bounds.

        constraint set to :py:attr:`kStrict_SrcRectConstraint` limits
        :py:class:`Paint` :py:class:`FilterQuality` to sample within isrc; set
        to :py:attr:`kFast_SrcRectConstraint` allows sampling outside to improve
        performance.

        :bitmap: :py:class:`Bitmap` containing pixels, dimensions, and format
        :dst: destination :py:class:`Rect` of image to draw to
        :paint: :py:class:`Paint` containing :py:class:`BlendMode`,
            :py:class:`ColorFilter`, :py:class:`ImageFilter`, and so on; or
            nullptr
        :constraint: filter strictly within bitmap or draw faster
        )docstring",
        py::arg("bitmap"), py::arg("dst"), py::arg("paint") = nullptr,
        py::arg("constraint") =
            SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint)
    // .def("drawImageLattice", &SkCanvas::drawImageLattice,
    //     "Draws SkImage image stretched proportionally to fit into SkRect dst.")
    // .def("experimental_DrawEdgeAAQuad",
    //     py::overload_cast<const SkRect&, const SkPoint[4],
    //         SkCanvas::QuadAAFlags, const SkColor4f&, SkBlendMode>(
    //             &SkCanvas::experimental_DrawEdgeAAQuad),
    //     "This is an experimental API for the SkiaRenderer Chromium project, "
    //     "and its API will surely evolve if it is not removed outright.")
    // .def("experimental_DrawEdgeAAQuad",
    //     py::overload_cast<const SkRect&, const SkPoint[4],
    //         SkCanvas::QuadAAFlags, SkColor, SkBlendMode>(
    //             &SkCanvas::experimental_DrawEdgeAAQuad))
    .def("drawSimpleText",
        // &SkCanvas::drawSimpleText,
        [] (SkCanvas& canvas, const std::string& text, SkScalar x, SkScalar y,
            const SkFont& font, const SkPaint& paint) {
            canvas.drawSimpleText(
                text.c_str(), text.size(), SkTextEncoding::kUTF8, x, y, font,
                paint);
        },
        R"docstring(
        Draws text, with origin at (x, y), using clip, :py:class:`Matrix`,
        :py:class:`Font` font, and :py:class:`Paint` paint.

        This function uses the default character-to-glyph mapping from the
        :py:class:`Typeface` in font. It does not perform typeface fallback for
        characters not found in the :py:class:`Typeface`. It does not perform
        kerning or other complex shaping; glyphs are positioned based on their
        default advances.

        Text size is affected by :py:class:`Matrix` and :py:class:`Font` text
        size. Default text size is 12 point.

        All elements of paint: :py:class:`PathEffect`, :py:class:`MaskFilter`,
        :py:class:`Shader`, :py:class:`ColorFilter`, :py:class:`ImageFilter`,
        and :py:class:`DrawLooper`; apply to text. By default, draws filled
        black glyphs.

        :param str text: character code points or glyphs drawn
        :param float x: start of text on x-axis
        :param float y: start of text on y-axis
        :param skia.Font font: typeface, text size and so, used to describe the
            text
        :param skia.Paint paint: blend, color, and so on, used to draw
        )docstring",
        py::arg("text"), py::arg("x"), py::arg("y"), py::arg("font"),
        py::arg("paint"))
    .def("drawString",
        // py::overload_cast<const char[], SkScalar, SkScalar, const SkFont&,
        //     const SkPaint&>(&SkCanvas::drawString),
        [] (SkCanvas& canvas, const std::string& text, SkScalar x, SkScalar y,
            const SkFont& font, const SkPaint& paint) {
            canvas.drawString(text.c_str(), x, y, font, paint);
        },
        R"docstring(
        Draws string, with origin at (x, y), using clip, :py:class:`Matrix`,
        :py:class:`Font` font, and :py:class:`Paint` paint.

        This function uses the default character-to-glyph mapping from the
        :py:class:`Typeface` in font. It does not perform typeface fallback for
        characters not found in the :py:class:`Typeface`. It does not perform
        kerning; glyphs are positioned based on their default advances.

        String `text` is encoded as UTF-8.

        Text size is affected by :py:class:`Matrix` and font text size. Default
        text size is 12 point.

        All elements of paint: :py:class:`PathEffect`, :py:class:`MaskFilter`,
        :py:class:`Shader`, :py:class:`ColorFilter`, :py:class:`ImageFilter`,
        and :py:class:`DrawLooper`; apply to text. By default, draws filled
        black glyphs.

        :param str text: character code points drawn, ending with a char value
            of zero
        :param float x: start of string on x-axis
        :param float y: start of string on y-axis
        :param skia.Font font: typeface, text size and so, used to describe the
            text
        :param skia.Paint paint: blend, color, and so on, used to draw
        )docstring",
        py::arg("text"), py::arg("x"), py::arg("y"), py::arg("font"),
        py::arg("paint"))
    // .def("drawString",
    //     py::overload_cast<const SkString&, SkScalar, SkScalar, const SkFont&,
    //         const SkPaint&>(&SkCanvas::drawString),
    //     "DDraws SkString, with origin at (x, y), using clip, SkMatrix, SkFont "
    //     "font, and SkPaint paint.")
    .def("drawTextBlob",
        py::overload_cast<const SkTextBlob*, SkScalar, SkScalar,
            const SkPaint&>(&SkCanvas::drawTextBlob),
        R"docstring(
        Draws :py:class:`TextBlob` blob at (x, y), using clip,
        :py:class:`Matrix`, and :py:class:`Paint` paint.

        `blob` contains glyphs, their positions, and paint attributes specific
        to text: :py:class:`Typeface`, :py:class:`Paint` text size,
        :py:class:`Paint` text scale x, :py:class:`Paint` text skew x,
        :py:class:`Paint`::Align, :py:class:`Paint`::Hinting, anti-alias,
        :py:class:`Paint` fake bold, :py:class:`Paint` font embedded bitmaps,
        :py:class:`Paint` full hinting spacing, LCD text, :py:class:`Paint`
        linear text, and :py:class:`Paint` subpixel text.

        :py:class:`TextEncoding` must be set to
        :py:attr:`TextEncoding.kGlyphID`.

        Elements of paint: anti-alias, :py:class:`BlendMode`, color including
        alpha, :py:class:`ColorFilter`, :py:class:`Paint` dither,
        :py:class:`DrawLooper`, :py:class:`MaskFilter`, :py:class:`PathEffect`,
        :py:class:`Shader`, and :py:class:`Paint.Style`; apply to blob. If
        :py:class:`Paint` contains :py:attr:`Paint.kStroke`: :py:class:`Paint`
        iter limit, :py:class:`Paint.Cap`, :py:class:`Paint.Join`, and
        :py:class:`Paint` stroke width; apply to :py:class:`Path` created from
        blob.

        :param skia.TextBlob blob: glyphs, positions, and their paints' text
            size, typeface, and so on
        :param float x: horizontal offset applied to blob
        :param float y: vertical offset applied to blob
        :param skia.Paint paint: blend, color, stroking, and so on, used to draw
        )docstring",
        py::arg("blob"), py::arg("x"), py::arg("y"), py::arg("paint"))
    // .def("drawTextBlob",
    //     py::overload_cast<const sk_sp<SkTextBlob>&, SkScalar, SkScalar,
    //         const SkPaint&>(&SkCanvas::drawTextBlob),
    //     "Draws SkTextBlob blob at (x, y), using clip, SkMatrix, and SkPaint "
    //     "paint.")
    // .def("drawPicture",
    //     py::overload_cast<const SkPicture*>(&SkCanvas::drawPicture))
    // .def("drawPicture",
    //     py::overload_cast<const sk_sp<SkPicture>&>(&SkCanvas::drawPicture),
    //     "Draws SkPicture picture, using clip and SkMatrix.")
    .def("drawPicture",
        py::overload_cast<const SkPicture*, const SkMatrix*, const SkPaint*>(
            &SkCanvas::drawPicture),
        R"docstring(
        Draws :py:class:`Picture` picture, using clip and :py:class:`Matrix`;
        transforming picture with :py:class:`Matrix` matrix, if provided; and
        use :py:class:`Paint` paint alpha, :py:class:`ColorFilter`,
        :py:class:`ImageFilter`, and :py:class:`BlendMode`, if provided.

        :py:class:`Picture` records a series of draw commands for later
        playback.

        matrix transformation is equivalent to: save(), concat(), drawPicture(),
        restore(). paint use is equivalent to: saveLayer(), drawPicture(),
        restore().

        :param skia.Picture picture: recorded drawing commands to play
        :param skia.Matrix matrix:  :py:class:`Matrix` to rotate, scale,
            translate, and so on; may be `None`
        :param skia.Paint paint: :py:class:`Paint` to apply transparency,
            filtering, and so on; may be `None`
        )docstring",
        py::arg("picture"), py::arg("matrix") = nullptr,
        py::arg("paint") = nullptr)
    // .def("drawPicture",
    //     py::overload_cast<const sk_sp<SkPicture>&, const SkMatrix*,
    //         const SkPaint*>(&SkCanvas::drawPicture),
    //     "Draws SkPicture picture, using clip and SkMatrix; transforming "
    //     "picture with SkMatrix matrix, if provided; and use SkPaint paint "
    //     "alpha, SkColorFilter, SkImageFilter, and SkBlendMode, if provided.")
    .def("drawVertices",
        // py::overload_cast<const SkVertices*, SkBlendMode, const SkPaint&>(
        //     &SkCanvas::drawVertices),
        [] (SkCanvas& canvas, const SkVertices* vertices, const SkPaint& paint,
            SkBlendMode mode) { canvas.drawVertices(vertices, mode, paint); },
        R"docstring(
        Draws :py:class:`Vertices` vertices, a triangle mesh, using clip and
        :py:class:`Matrix`. If paint contains an :py:class:`Shader` and vertices
        does not contain texCoords, the shader is mapped using the vertices'
        positions.

        If vvertices colors are defined in vertices, and :py:class:`Paint` paint
        contains :py:class:`Shader`, :py:class:`BlendMode` mode combines
        vertices colors with :py:class:`Shader`.

        :param skia.Vertices vertices: triangle mesh to draw
        :param skia.BlendMode mode: combines vertices colors with
            :py:class:`Shader`, if both are present
        :param skia.Paint paint: specifies the :py:class:`Shader`, used as
            :py:class:`Vertices` texture
        )docstring",
        py::arg("vertices"), py::arg("paint"),
        py::arg("mode") = SkBlendMode::kModulate)
    // .def("drawVertices",
    //     py::overload_cast<const SkVertices*, const SkPaint&>(
    //         &SkCanvas::drawVertices),
    //     "Variant of 3-parameter drawVertices, using the default of Modulate "
    //     "for the blend parameter.")
    // .def("drawVertices",
    //     py::overload_cast<const sk_sp<SkVertices>&, SkBlendMode,
    //         const SkPaint&>(&SkCanvas::drawVertices),
    //     "Draws SkVertices vertices, a triangle mesh, using clip and SkMatrix.")
    // .def("drawVertices",
    //     py::overload_cast<const sk_sp<SkVertices>&, const SkPaint&>(
    //         &SkCanvas::drawVertices),
    //     "Variant of 3-parameter drawVertices, using the default of Modulate "
    //     "for the blend parameter.")
    .def("drawPatch",
        // py::overload_cast<const SkPoint[12], const SkColor[4],
        //     const SkPoint[4], SkBlendMode, const SkPaint&>(
        //         &SkCanvas::drawPatch),
        [] (SkCanvas& canvas, const std::vector<SkPoint>& cubics,
            const std::vector<SkColor>& colors,
            const std::vector<SkPoint>& texCoords,
            SkBlendMode mode, const SkPaint& paint) {
            if (cubics.size() != 12)
                throw std::runtime_error("cubics must have 12 elements");
            if (colors.size() != 4)
                throw std::runtime_error("colors must have 4 elements");
            if (!(texCoords.size() == 4 || texCoords.empty()))
                throw std::runtime_error("texCoords must have 0 or 4 elements");

            canvas.drawPatch(
                &cubics[0], &colors[0],
                (texCoords.empty()) ? nullptr : &texCoords[0], mode, paint);
        },
        R"docstring(
        Draws a Coons patch: the interpolation of four cubics with shared
        corners, associating a color, and optionally a texture
        :py:class:`Point`, with each corner.

        Coons patch uses clip and :py:class:`Matrix`, paint :py:class:`Shader`,
        :py:class:`ColorFilter`, alpha, :py:class:`ImageFilter`, and
        :py:class:`BlendMode`. If :py:class:`Shader` is provided it is treated
        as Coons patch texture; :py:class:`BlendMode` mode combines color colors
        and :py:class:`Shader` if both are provided.

        :py:class:`Point` array cubics specifies four :py:class:`Path` cubic
        starting at the top-left corner, in clockwise order, sharing every
        fourth point. The last :py:class:`Path` cubic ends at the first point.

        Color array color associates colors with corners in top-left, top-right,
        bottom-right, bottom-left order.

        If paint contains :py:class:`Shader`, :py:class:`Point` array texCoords
        maps :py:class:`Shader` as texture to corners in top-left, top-right,
        bottom-right, bottom-left order. If texCoords is nullptr,
        :py:class:`Shader` is mapped using positions (derived from cubics).

        :param List[skia.Point] cubics: :py:class:`Path` cubic array, sharing
            common points (length 12)
        :param List[int] colors: color array, one for each corner (length 4)
        :param List[skia.Point] texCoords: :py:class:`Point` array of texture
            coordinates, mapping :py:class:`Shader` to corners (length 4); may
            be an empty list
        :param mode: :py:class:`BlendMode` for colors, and for
            :py:class:`Shader` if paint has one
        :param paint: :py:class:`Shader`, :py:class:`ColorFilter`,
            :py:class:`BlendMode`, used to draw
        )docstring",
        py::arg("cubics"), py::arg("colors"), py::arg("texCoords"),
        py::arg("mode"), py::arg("paint"))
    // .def("drawPatch",
    //     py::overload_cast<const SkPoint[12], const SkColor[4],
    //         const SkPoint[4], const SkPaint&>(
    //             &SkCanvas::drawPatch),
    //     "Draws SkPath cubic Coons patch: the interpolation of four cubics with "
    //     "shared corners, associating a color, and optionally a texture "
    //     "SkPoint, with each corner.")
    .def("drawAtlas",
        // py::overload_cast<const SkImage*, const SkRSXform[], const SkRect[],
        //     const SkColor[], int, SkBlendMode, const SkRect*, const SkPaint*>(
        //         &SkCanvas::drawAtlas),
        [] (SkCanvas& canvas, const SkImage* atlas,
            const std::vector<SkRSXform>& xform,
            const std::vector<SkRect>& tex,
            const std::vector<SkColor>& colors,
            SkBlendMode mode, const SkRect* cullRect, const SkPaint* paint) {
            if (xform.size() != tex.size())
                throw std::runtime_error(
                    "xform and tex must have the same length.");
            if (!colors.empty() && colors.size() != xform.size())
                throw std::runtime_error(
                    "colors must have the same length with xform.");
            canvas.drawAtlas(atlas, &xform[0], &tex[0],
                (colors.empty()) ? nullptr : &colors[0],
                xform.size(), mode, cullRect, paint);
        },
        R"docstring(
        Draws a set of sprites from atlas, using clip, :py:class:`Matrix`, and
        optional :py:class:`Paint` paint.

        paint uses anti-alias, alpha, :py:class:`ColorFilter`,
        :py:class:`ImageFilter`, and :py:class:`BlendMode` to draw, if present.
        For each entry in the array, :py:class:`Rect` tex locates sprite in
        atlas, and :py:class:`RSXform` xform transforms it into destination
        space.

        xform, text, and colors if present, must contain count entries. Optional
        colors are applied for each sprite using :py:class:`BlendMode` mode,
        treating sprite as source and colors as destination. Optional cullRect
        is a conservative bounds of all transformed sprites. If cullRect is
        outside of clip, canvas can skip drawing.

        If atlas is `None`, this draws nothing.

        :param skia.Image atlas: :py:class:`Image` containing sprites
        :param List[skia.RSXform] xform: :py:class:`RSXform` mappings for
            sprites in atlas
        :param List[skia.Rect] tex: :py:class:`Rect` locations of sprites in
            atlas
        :param List[int] colors: one per sprite, blended with sprite using
            :py:class:`BlendMode`; may be `None`
        :param skia.BlendMode mode: :py:class:`BlendMode` combining colors and
            sprites
        :param Union[skia.Rect,None] cullRect: bounds of transformed sprites
            for efficient clipping; may be `None`
        :param Union[skia.Paint,None] paint: :py:class:`ColorFilter`,
            :py:class:`ImageFilter`, :py:class:`BlendMode`, and so on; may be
            `None`
        )docstring",
        py::arg("atlas"), py::arg("xform"), py::arg("tex"), py::arg("colors"),
        py::arg("mode"), py::arg("cullRect") = nullptr,
        py::arg("paint") = nullptr)
    // .def("drawAtlas",
    //     py::overload_cast<const sk_sp<SkImage>&, const SkRSXform[],
    //         const SkRect[], const SkColor[], int, SkBlendMode, const SkRect*,
    //         const SkPaint*>(&SkCanvas::drawAtlas),
    //     "Draws a set of sprites from atlas, using clip, SkMatrix, and optional "
    //     "SkPaint paint.")
    // .def("drawAtlas",
    //     py::overload_cast<const SkImage*, const SkRSXform[], const SkRect[],
    //         int, const SkRect*, const SkPaint*>(
    //             &SkCanvas::drawAtlas),
    //     "Draws a set of sprites from atlas, using clip, SkMatrix, and optional "
    //     "SkPaint paint.")
    // .def("drawAtlas",
    //     py::overload_cast<const sk_sp<SkImage>&, const SkRSXform[],
    //         const SkRect[], int, const SkRect*, const SkPaint*>(
    //             &SkCanvas::drawAtlas),
    //     "Draws a set of sprites from atlas, using clip, SkMatrix, and optional "
    //     "SkPaint paint.")
    // .def("drawDrawable",
    //     py::overload_cast<SkDrawable*, const SkMatrix*>(
    //         &SkCanvas::drawDrawable),
    //     "Draws SkDrawable drawable using clip and SkMatrix, concatenated with "
    //     "optional matrix.")
    // .def("drawDrawable",
    //     py::overload_cast<SkDrawable*, SkScalar, SkScalar>(
    //         &SkCanvas::drawDrawable),
    //     "Draws SkDrawable drawable using clip and SkMatrix, offset by (x, y).")
    // .def("drawAnnotation",
    //     py::overload_cast<const SkRect&, const char[], SkData*>(
    //         &SkCanvas::drawAnnotation),
    //     "Associates SkRect on SkCanvas with an annotation; a key-value pair, "
    //     "where the key is a null-terminated UTF-8 string, and optional value "
    //     "is stored as SkData.")
    .def("drawAnnotation",
        // py::overload_cast<const SkRect&, const char[], const sk_sp<SkData>&>(
        //     &SkCanvas::drawAnnotation),
        [] (SkCanvas& canvas, const SkRect& rect, const std::string& key,
            const sk_sp<SkData>& value) {
            canvas.drawAnnotation(rect, key.c_str(), value);
        },
        R"docstring(
        Associates :py:class:`Rect` on :py:class:`Canvas` when an annotation; a
        key-value pair, where the key is an UTF-8 string, and optional value is
        stored as :py:class:`Data`.

        Only some canvas implementations, such as recording to
        :py:class:`Picture`, or drawing to document PDF, use annotations.

        :param skia.Rect rect: :py:class:`Rect` extent of canvas to annotate
        :param str key: string used for lookup
        :param skia.Data value: data holding value stored in annotation
        )docstring",
        py::arg("rect"), py::arg("key"), py::arg("value"))
    .def("isClipEmpty", &SkCanvas::isClipEmpty,
        R"docstring(
        Returns true if clip is empty; that is, nothing will draw.

        May do work when called; it should not be called more often than needed.
        However, once called, subsequent calls perform no work until clip
        changes.

        :return: true if clip is empty
        )docstring")
    .def("isClipRect", &SkCanvas::isClipRect,
        R"docstring(
        Returns true if clip is :py:class:`Rect` and not empty.

        Returns false if the clip is empty, or if it is not :py:class:`Rect`.

        :return: true if clip is :py:class:`Rect` and not empty
        )docstring")
    .def("getTotalMatrix", &SkCanvas::getTotalMatrix,
        R"docstring(
        Legacy version of :py:meth:`getLocalToDevice`, which strips away any Z
        information, and just returns a 3x3 version.

        :return: 3x3 version of :py:meth:`getLocalToDevice`
        )docstring")
    .def("getLocalToDevice",
        py::overload_cast<>(&SkCanvas::getLocalToDevice, py::const_))
    // .def("getLocalToDevice",
    //     py::overload_cast<SkScalar[16]>(
    //         &SkCanvas::getLocalToDevice, py::const_))
    // .def("experimental_getLocalToWorld",
    //     py::overload_cast<>(
    //         &SkCanvas::experimental_getLocalToWorld, py::const_))
    // .def("experimental_getLocalToCamera",
    //     py::overload_cast<>(
    //         &SkCanvas::experimental_getLocalToCamera, py::const_))
    // .def("experimental_getLocalToCamera",
    //     py::overload_cast<SkScalar[16]>(
    //         &SkCanvas::experimental_getLocalToCamera, py::const_))
    // .def("experimental_getLocalToWorld",
    //     py::overload_cast<SkScalar[16]>(
    //         &SkCanvas::experimental_getLocalToWorld, py::const_))
    // Static methods.
    .def_static("MakeRasterDirect",
        // &SkCanvas::MakeRasterDirect,
        [](const SkImageInfo& imageInfo, py::buffer pixels, size_t srcRowBytes,
            const SkSurfaceProps* surfaceProps) {
            py::buffer_info info = pixels.request();
            auto rowBytes = ValidateBufferToImageInfo(
                imageInfo, info, srcRowBytes);
            auto canvas = SkCanvas::MakeRasterDirect(
                imageInfo, info.ptr, rowBytes, surfaceProps);
            if (!canvas)
                throw std::runtime_error("Failed to create Canvas");
            return canvas;
        },
        R"docstring(
        Allocates raster :py:class:`Canvas` that will draw directly into pixels.

        :py:class:`Canvas` is returned if all parameters are valid. Valid
        parameters include: info dimensions are zero or positive; info contains
        :py:class:`ColorType` and :py:class:`AlphaType` supported by raster
        surface; pixels is buffer object of sufficient length; rowBytes is zero
        or large enough to contain info width pixels of :py:class:`ColorType`.

        Pass zero for rowBytes to compute rowBytes from info width and size of
        pixel. If rowBytes is greater than zero, it must be equal to or greater
        than info width times bytes required for :py:class:`ColorType`.

        Pixel buffer size should be info height times computed rowBytes. Pixels
        are not initialized. To access pixels after drawing, call flush() or
        peekPixels().

        :param skia.ImageInfo info: width, height, :py:class:`ColorType`,
            :py:class:`AlphaType`, :py:class:`ColorSpace`, of raster surface;
            width, or height, or both, may be zero
        :param Union[bytes,bytearray,memoryview] pixels: destination pixels
            buffer
        :param int rowBytes: interval from one :py:class:`Surface` row to the
            next, or zero
        :param skia.SurfaceProps props: LCD striping orientation and setting for
            device independent fonts; may be `None`
        )docstring",
        py::arg("imageInfo"), py::arg("pixels"), py::arg("rowBytes") = 0,
        py::arg("surfaceProps") = nullptr)
    .def_static("MakeRasterDirectN32",
        [](int width, int height, py::buffer pixels, size_t srcRowBytes) {
            auto info = pixels.request();
            auto imageInfo = SkImageInfo::MakeN32Premul(width, height);
            auto rowBytes = ValidateBufferToImageInfo(
                imageInfo, info, srcRowBytes);
            auto canvas = SkCanvas::MakeRasterDirectN32(
                width, height, static_cast<SkPMColor*>(info.ptr), rowBytes);
            if (!canvas)
                throw std::runtime_error("Failed to create Canvas");
            return canvas;
        },
        R"docstring(
        Allocates raster :py:class:`Canvas` specified by inline image
        specification.

        Subsequent :py:class:`Canvas` calls draw into pixels.
        :py:class:`ColorType` is set to :py:attr:`ColorType.kN32_ColorType`.
        :py:class:`AlphaType` is set to :py:attr:`AlphaType.kPremul_AlphaType`.
        To access pixels after drawing, call flush() or peekPixels().

        :py:class:`Canvas` is returned if all parameters are valid. Valid
        parameters include: width and height are zero or positive; pixels is
        buffer object with sufficient length; rowBytes is zero or large enough
        to contain width pixels of :py:attr:`ColorType.kN32_ColorType`.

        Pass zero for rowBytes to compute rowBytes from width and size of pixel.
        If rowBytes is greater than zero, it must be equal to or greater than
        width times bytes required for :py:class:`ColorType`.

        Pixel buffer size should be height times rowBytes.

        :param int width: pixel column count on raster surface created; must be
            zero or greater
        :param int height: pixel row count on raster surface created; must be
            zero or greater
        :param Union[bytes,bytearray,memoryview] pixels: pointer to destination
            pixels buffer; buffer size should be height times rowBytes
        :param int rowBytes: interval from one :py:class:`Surface` row to the
            next, or zero
        )docstring",
        py::arg("width"), py::arg("height"), py::arg("pixels"),
        py::arg("rowBytes") = 0)
    ;

    m.def("MakeNullCanvas", &SkMakeNullCanvas);

py::class_<SkSVGCanvas>(m, "SVGCanvas")
    .def_static("Make", &SkSVGCanvas::Make,
        R"docstring(
        Returns a new canvas that will generate SVG commands from its draw
        calls, and send them to the provided stream. Ownership of the stream is
        not transfered, and it must remain valid for the lifetime of the
        returned canvas::

            stream = skia.FILEWStream('output.svg')
            canvas = skia.SVGCanvas.Make((640, 480), stream)
            draw(canvas)
            # Make sure to delete the canvas before the stream goes out of scope
            del canvas
            stream.flush()

        The canvas may buffer some drawing calls, so the output is not
        guaranteed to be valid or complete until the canvas instance is deleted.

        The 'bounds' parameter defines an initial SVG viewport (viewBox
        attribute on the root SVG element).
        )docstring",
        py::arg("bounds"), py::arg("stream"), py::arg("flags") = 0)
    .def_property_readonly_static("kConvertTextToPaths_Flag",
        [] (py::object obj) {
            return uint32_t(SkSVGCanvas::kConvertTextToPaths_Flag);
        })
    .def_property_readonly_static("kNoPrettyXML_Flag",
        [] (py::object obj) {
            return uint32_t(SkSVGCanvas::kNoPrettyXML_Flag);
        })
    ;
}
